mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-29 02:28:13 +08:00
Code review previous commit, and improve G/R/S hints
This commit is contained in:
+1
-2
@@ -30,5 +30,4 @@ cargo install cargo-about
|
|||||||
# Build for production
|
# Build for production
|
||||||
echo 👷 Build Graphite web client
|
echo 👷 Build Graphite web client
|
||||||
export NODE_ENV=production
|
export NODE_ENV=production
|
||||||
npm run build
|
npm run build && mv public dist # `&&` is used here to preserve the exit code
|
||||||
mv public dist
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
|
||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
use graphene_core::vector::style::{Fill, Stroke};
|
use graphene_core::vector::style::{Fill, Stroke};
|
||||||
use graphene_core::vector::ManipulatorPointId;
|
use graphene_core::vector::ManipulatorPointId;
|
||||||
|
|
||||||
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
pub type LayerIdentifier = Vec<document_legacy::LayerId>;
|
pub type LayerIdentifier = Vec<document_legacy::LayerId>;
|
||||||
|
|
||||||
#[impl_message(Message, DocumentMessage, GraphOperation)]
|
#[impl_message(Message, DocumentMessage, GraphOperation)]
|
||||||
|
|||||||
+14
-9
@@ -1,14 +1,14 @@
|
|||||||
|
use super::{resolve_document_node_type, VectorDataModification};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use document_legacy::document::Document;
|
use document_legacy::document::Document;
|
||||||
use document_legacy::{LayerId, Operation};
|
use document_legacy::{LayerId, Operation};
|
||||||
use glam::{DAffine2, DVec2};
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork};
|
use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork};
|
||||||
use graphene_core::vector::style::{Fill, FillType, Stroke};
|
use graphene_core::vector::style::{Fill, FillType, Stroke};
|
||||||
use transform_utils::LayerBounds;
|
use transform_utils::LayerBounds;
|
||||||
|
|
||||||
use super::{resolve_document_node_type, VectorDataModification};
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
mod transform_utils;
|
mod transform_utils;
|
||||||
|
|
||||||
@@ -37,7 +37,8 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
let document_node = self.network.nodes.get_mut(&node_id).unwrap();
|
let document_node = self.network.nodes.get_mut(&node_id).unwrap();
|
||||||
update_input(&mut document_node.inputs);
|
update_input(&mut document_node.inputs);
|
||||||
}
|
}
|
||||||
/// Insert a new node and modify the inputs
|
|
||||||
|
/// Inserts a new node and modifies the inputs
|
||||||
fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
|
fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
|
||||||
let output_node_id = self.network.outputs[0].node_id;
|
let output_node_id = self.network.outputs[0].node_id;
|
||||||
let Some(output_node) = self.network.nodes.get_mut(&output_node_id) else {
|
let Some(output_node) = self.network.nodes.get_mut(&output_node_id) else {
|
||||||
@@ -73,6 +74,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
let layer_path = self.layer.to_vec();
|
let layer_path = self.layer.to_vec();
|
||||||
self.responses.add(DocumentMessage::NodeGraphFrameGenerate { layer_path });
|
self.responses.add(DocumentMessage::NodeGraphFrameGenerate { layer_path });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_set(&mut self, fill: Fill) {
|
fn fill_set(&mut self, fill: Fill) {
|
||||||
self.modify_inputs("Fill", |inputs| {
|
self.modify_inputs("Fill", |inputs| {
|
||||||
let fill_type = match fill {
|
let fill_type = match fill {
|
||||||
@@ -94,6 +96,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stroke_set(&mut self, stroke: Stroke) {
|
fn stroke_set(&mut self, stroke: Stroke) {
|
||||||
self.modify_inputs("Stroke", |inputs| {
|
self.modify_inputs("Stroke", |inputs| {
|
||||||
inputs[1] = NodeInput::value(TaggedValue::Color(stroke.color.unwrap_or_default()), false);
|
inputs[1] = NodeInput::value(TaggedValue::Color(stroke.color.unwrap_or_default()), false);
|
||||||
@@ -114,11 +117,12 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
TransformIn::Scope { scope } => scope * parent_transform,
|
TransformIn::Scope { scope } => scope * parent_transform,
|
||||||
TransformIn::Viewport => parent_transform,
|
TransformIn::Viewport => parent_transform,
|
||||||
};
|
};
|
||||||
let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalised_pivot(inputs)));
|
let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs)));
|
||||||
let transform = to.inverse() * pivot.inverse() * transform * pivot * to * layer_transform;
|
let transform = to.inverse() * pivot.inverse() * transform * pivot * to * layer_transform;
|
||||||
transform_utils::update_transform(inputs, transform);
|
transform_utils::update_transform(inputs, transform);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds) {
|
fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds) {
|
||||||
self.modify_inputs("Transform", |inputs| {
|
self.modify_inputs("Transform", |inputs| {
|
||||||
let to = match transform_in {
|
let to = match transform_in {
|
||||||
@@ -126,15 +130,16 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
TransformIn::Scope { scope } => scope * parent_transform,
|
TransformIn::Scope { scope } => scope * parent_transform,
|
||||||
TransformIn::Viewport => parent_transform,
|
TransformIn::Viewport => parent_transform,
|
||||||
};
|
};
|
||||||
let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalised_pivot(inputs)));
|
let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs)));
|
||||||
let transform = to.inverse() * pivot.inverse() * transform * pivot;
|
let transform = to.inverse() * pivot.inverse() * transform * pivot;
|
||||||
transform_utils::update_transform(inputs, transform);
|
transform_utils::update_transform(inputs, transform);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) {
|
fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) {
|
||||||
self.modify_inputs("Transform", |inputs| {
|
self.modify_inputs("Transform", |inputs| {
|
||||||
let layer_transform = transform_utils::get_current_transform(inputs);
|
let layer_transform = transform_utils::get_current_transform(inputs);
|
||||||
let old_pivot_transform = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalised_pivot(inputs)));
|
let old_pivot_transform = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs)));
|
||||||
let new_pivot_transform = DAffine2::from_translation(bounds.local_pivot(new_pivot));
|
let new_pivot_transform = DAffine2::from_translation(bounds.local_pivot(new_pivot));
|
||||||
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
|
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
|
||||||
transform_utils::update_transform(inputs, transform);
|
transform_utils::update_transform(inputs, transform);
|
||||||
@@ -172,10 +177,10 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
});
|
});
|
||||||
self.modify_inputs("Transform", |inputs| {
|
self.modify_inputs("Transform", |inputs| {
|
||||||
let layer_transform = transform_utils::get_current_transform(inputs);
|
let layer_transform = transform_utils::get_current_transform(inputs);
|
||||||
let normalised_pivot = transform_utils::get_current_normalised_pivot(inputs);
|
let normalized_pivot = transform_utils::get_current_normalized_pivot(inputs);
|
||||||
|
|
||||||
let old_layerspace_pivot = (old_bounds_max - old_bounds_min) * normalised_pivot + old_bounds_min;
|
let old_layerspace_pivot = (old_bounds_max - old_bounds_min) * normalized_pivot + old_bounds_min;
|
||||||
let new_layerspace_pivot = (new_bounds_max - new_bounds_min) * normalised_pivot + new_bounds_min;
|
let new_layerspace_pivot = (new_bounds_max - new_bounds_min) * normalized_pivot + new_bounds_min;
|
||||||
let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot);
|
let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot);
|
||||||
let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot);
|
let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot);
|
||||||
|
|
||||||
|
|||||||
+38
-21
@@ -6,7 +6,7 @@ use graph_craft::document::{value::TaggedValue, NodeInput};
|
|||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
use graphene_core::vector::{ManipulatorPointId, SelectedType};
|
use graphene_core::vector::{ManipulatorPointId, SelectedType};
|
||||||
|
|
||||||
/// Convert an affine transform into scale angle translation and shear, assuming shear.y = 0.
|
/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`.
|
||||||
pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64, DVec2, DVec2) {
|
pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64, DVec2, DVec2) {
|
||||||
let x_axis = transform.matrix2.x_axis;
|
let x_axis = transform.matrix2.x_axis;
|
||||||
let y_axis = transform.matrix2.y_axis;
|
let y_axis = transform.matrix2.y_axis;
|
||||||
@@ -33,15 +33,15 @@ pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64
|
|||||||
|
|
||||||
/// Update the inputs of the transform node to match a new transform
|
/// Update the inputs of the transform node to match a new transform
|
||||||
pub fn update_transform(inputs: &mut [NodeInput], transform: DAffine2) {
|
pub fn update_transform(inputs: &mut [NodeInput], transform: DAffine2) {
|
||||||
let (scale, angle, translation, skew) = compute_scale_angle_translation_shear(transform);
|
let (scale, angle, translation, shear) = compute_scale_angle_translation_shear(transform);
|
||||||
|
|
||||||
inputs[1] = NodeInput::value(TaggedValue::DVec2(translation), false);
|
inputs[1] = NodeInput::value(TaggedValue::DVec2(translation), false);
|
||||||
inputs[2] = NodeInput::value(TaggedValue::F64(angle), false);
|
inputs[2] = NodeInput::value(TaggedValue::F64(angle), false);
|
||||||
inputs[3] = NodeInput::value(TaggedValue::DVec2(scale), false);
|
inputs[3] = NodeInput::value(TaggedValue::DVec2(scale), false);
|
||||||
inputs[4] = NodeInput::value(TaggedValue::DVec2(skew), false);
|
inputs[4] = NodeInput::value(TaggedValue::DVec2(shear), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: This should be extracted from the graph at the location of the transform node.
|
// TODO: This should be extracted from the graph at the location of the transform node.
|
||||||
pub struct LayerBounds {
|
pub struct LayerBounds {
|
||||||
pub bounds: [DVec2; 2],
|
pub bounds: [DVec2; 2],
|
||||||
pub bounds_transform: DAffine2,
|
pub bounds_transform: DAffine2,
|
||||||
@@ -64,11 +64,13 @@ impl LayerBounds {
|
|||||||
layer_transform,
|
layer_transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn layerspace_pivot(&self, normalised_pivot: DVec2) -> DVec2 {
|
|
||||||
self.bounds[0] + (self.bounds[1] - self.bounds[0]) * normalised_pivot
|
pub fn layerspace_pivot(&self, normalized_pivot: DVec2) -> DVec2 {
|
||||||
|
self.bounds[0] + (self.bounds[1] - self.bounds[0]) * normalized_pivot
|
||||||
}
|
}
|
||||||
pub fn local_pivot(&self, normalised_pivot: DVec2) -> DVec2 {
|
|
||||||
self.bounds_transform.transform_point2(self.layerspace_pivot(normalised_pivot))
|
pub fn local_pivot(&self, normalized_pivot: DVec2) -> DVec2 {
|
||||||
|
self.bounds_transform.transform_point2(self.layerspace_pivot(normalized_pivot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||||||
} else {
|
} else {
|
||||||
DVec2::ZERO
|
DVec2::ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
let angle = if let NodeInput::Value {
|
let angle = if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::F64(angle),
|
tagged_value: TaggedValue::F64(angle),
|
||||||
..
|
..
|
||||||
@@ -92,6 +95,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||||||
} else {
|
} else {
|
||||||
0.
|
0.
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale = if let NodeInput::Value {
|
let scale = if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::DVec2(scale),
|
tagged_value: TaggedValue::DVec2(scale),
|
||||||
..
|
..
|
||||||
@@ -101,6 +105,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||||||
} else {
|
} else {
|
||||||
DVec2::ONE
|
DVec2::ONE
|
||||||
};
|
};
|
||||||
|
|
||||||
let shear = if let NodeInput::Value {
|
let shear = if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::DVec2(shear),
|
tagged_value: TaggedValue::DVec2(shear),
|
||||||
..
|
..
|
||||||
@@ -110,11 +115,12 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||||||
} else {
|
} else {
|
||||||
DVec2::ZERO
|
DVec2::ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
DAffine2::from_scale_angle_translation(scale, angle, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.])
|
DAffine2::from_scale_angle_translation(scale, angle, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the current normalised pivot from the layer
|
/// Extract the current normalized pivot from the layer
|
||||||
pub fn get_current_normalised_pivot(inputs: &[NodeInput]) -> DVec2 {
|
pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
|
||||||
if let NodeInput::Value {
|
if let NodeInput::Value {
|
||||||
tagged_value: TaggedValue::DVec2(pivot),
|
tagged_value: TaggedValue::DVec2(pivot),
|
||||||
..
|
..
|
||||||
@@ -126,12 +132,16 @@ pub fn get_current_normalised_pivot(inputs: &[NodeInput]) -> DVec2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 
|
||||||
///
|
///
|
||||||
// \begin{bmatrix}
|
/// Source:
|
||||||
// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\
|
/// ```tex
|
||||||
// S_{x}\sin(\theta)+S_{y}\cos(\theta)H_{y} & S_{x}\sin(\theta)H_{x}+S_{y}\cos(\theta) & T_{y}\\
|
/// \begin{bmatrix}
|
||||||
// 0 & 0 & 1
|
/// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\
|
||||||
// \end{bmatrix}
|
/// S_{x}\sin(\theta)+S_{y}\cos(\theta)H_{y} & S_{x}\sin(\theta)H_{x}+S_{y}\cos(\theta) & T_{y}\\
|
||||||
|
/// 0 & 0 & 1
|
||||||
|
/// \end{bmatrix}
|
||||||
|
/// ```
|
||||||
#[test]
|
#[test]
|
||||||
fn derive_transform() {
|
fn derive_transform() {
|
||||||
for shear_x in -10..=10 {
|
for shear_x in -10..=10 {
|
||||||
@@ -147,19 +157,19 @@ fn derive_transform() {
|
|||||||
let scale = DVec2::new(scale_x, scale_y);
|
let scale = DVec2::new(scale_x, scale_y);
|
||||||
let translate = DVec2::new(5666., 644.);
|
let translate = DVec2::new(5666., 644.);
|
||||||
|
|
||||||
let origional_transform = DAffine2::from_cols(
|
let original_transform = DAffine2::from_cols(
|
||||||
DVec2::new(scale.x * angle.cos() - scale.y * angle.sin() * shear.y, scale.x * angle.sin() + scale.y * angle.cos() * shear.y),
|
DVec2::new(scale.x * angle.cos() - scale.y * angle.sin() * shear.y, scale.x * angle.sin() + scale.y * angle.cos() * shear.y),
|
||||||
DVec2::new(scale.x * angle.cos() * shear.x - scale.y * angle.sin(), scale.x * angle.sin() * shear.x + scale.y * angle.cos()),
|
DVec2::new(scale.x * angle.cos() * shear.x - scale.y * angle.sin(), scale.x * angle.sin() * shear.x + scale.y * angle.cos()),
|
||||||
translate,
|
translate,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (new_scale, new_angle, new_translation, new_shear) = compute_scale_angle_translation_shear(origional_transform);
|
let (new_scale, new_angle, new_translation, new_shear) = compute_scale_angle_translation_shear(original_transform);
|
||||||
let new_transform = DAffine2::from_scale_angle_translation(new_scale, new_angle, new_translation) * DAffine2::from_cols_array(&[1., new_shear.y, new_shear.x, 1., 0., 0.]);
|
let new_transform = DAffine2::from_scale_angle_translation(new_scale, new_angle, new_translation) * DAffine2::from_cols_array(&[1., new_shear.y, new_shear.x, 1., 0., 0.]);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
new_transform.abs_diff_eq(origional_transform, 1e-10),
|
new_transform.abs_diff_eq(original_transform, 1e-10),
|
||||||
"origional_transform {} new_transform {} / scale {} new_scale {} / angle {} new_angle {} / shear {} / new_shear {}",
|
"original_transform {} new_transform {} / scale {} new_scale {} / angle {} new_angle {} / shear {} / new_shear {}",
|
||||||
origional_transform,
|
original_transform,
|
||||||
new_transform,
|
new_transform,
|
||||||
scale,
|
scale,
|
||||||
new_scale,
|
new_scale,
|
||||||
@@ -194,7 +204,7 @@ fn subpath_bounds(subpaths: &[Subpath<ManipulatorGroupId>]) -> [DVec2; 2] {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns corners of all subpaths (but expanded to avoid div zero errors)
|
/// Returns corners of all subpaths (but expanded to avoid division-by-zero errors)
|
||||||
pub fn nonzero_subpath_bounds(subpaths: &[Subpath<ManipulatorGroupId>]) -> [DVec2; 2] {
|
pub fn nonzero_subpath_bounds(subpaths: &[Subpath<ManipulatorGroupId>]) -> [DVec2; 2] {
|
||||||
let [bounds_min, bounds_max] = subpath_bounds(subpaths);
|
let [bounds_min, bounds_max] = subpath_bounds(subpaths);
|
||||||
clamp_bounds(bounds_min, bounds_max)
|
clamp_bounds(bounds_min, bounds_max)
|
||||||
@@ -213,6 +223,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
let subpath = &mut self.subpaths[subpath_index];
|
let subpath = &mut self.subpaths[subpath_index];
|
||||||
subpath.insert_manipulator_group(subpath.len(), manipulator_group)
|
subpath.insert_manipulator_group(subpath.len(), manipulator_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, manipulator_group: ManipulatorGroup<ManipulatorGroupId>, after_id: ManipulatorGroupId) {
|
fn insert(&mut self, manipulator_group: ManipulatorGroup<ManipulatorGroupId>, after_id: ManipulatorGroupId) {
|
||||||
for subpath in self.subpaths.iter_mut() {
|
for subpath in self.subpaths.iter_mut() {
|
||||||
if let Some(index) = subpath.manipulator_index_from_id(after_id) {
|
if let Some(index) = subpath.manipulator_index_from_id(after_id) {
|
||||||
@@ -221,6 +232,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_group(&mut self, id: ManipulatorGroupId) {
|
fn remove_group(&mut self, id: ManipulatorGroupId) {
|
||||||
for subpath in self.subpaths.iter_mut() {
|
for subpath in self.subpaths.iter_mut() {
|
||||||
if let Some(index) = subpath.manipulator_index_from_id(id) {
|
if let Some(index) = subpath.manipulator_index_from_id(id) {
|
||||||
@@ -229,6 +241,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_point(&mut self, point: ManipulatorPointId) {
|
fn remove_point(&mut self, point: ManipulatorPointId) {
|
||||||
for subpath in self.subpaths.iter_mut() {
|
for subpath in self.subpaths.iter_mut() {
|
||||||
if point.manipulator_type == SelectedType::Anchor {
|
if point.manipulator_type == SelectedType::Anchor {
|
||||||
@@ -245,6 +258,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_mirror(&mut self, id: ManipulatorGroupId, mirror_angle: bool) {
|
fn set_mirror(&mut self, id: ManipulatorGroupId, mirror_angle: bool) {
|
||||||
if !mirror_angle {
|
if !mirror_angle {
|
||||||
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
|
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
|
||||||
@@ -252,6 +266,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
self.mirror_angle_groups.push(id);
|
self.mirror_angle_groups.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_mirror(&mut self, id: ManipulatorGroupId) {
|
fn toggle_mirror(&mut self, id: ManipulatorGroupId) {
|
||||||
if self.mirror_angle_groups.contains(&id) {
|
if self.mirror_angle_groups.contains(&id) {
|
||||||
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
|
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
|
||||||
@@ -259,6 +274,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
self.mirror_angle_groups.push(id);
|
self.mirror_angle_groups.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_position(&mut self, point: ManipulatorPointId, position: DVec2) {
|
fn set_position(&mut self, point: ManipulatorPointId, position: DVec2) {
|
||||||
for subpath in self.subpaths.iter_mut() {
|
for subpath in self.subpaths.iter_mut() {
|
||||||
if let Some(manipulator) = subpath.manipulator_mut_from_id(point.group) {
|
if let Some(manipulator) = subpath.manipulator_mut_from_id(point.group) {
|
||||||
@@ -285,6 +301,7 @@ impl<'a> VectorModificationState<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modify(&mut self, modification: VectorDataModification) {
|
pub fn modify(&mut self, modification: VectorDataModification) {
|
||||||
match modification {
|
match modification {
|
||||||
VectorDataModification::AddEndManipulatorGroup { subpath_index, manipulator_group } => self.insert_end(subpath_index, manipulator_group),
|
VectorDataModification::AddEndManipulatorGroup { subpath_index, manipulator_group } => self.insert_end(subpath_index, manipulator_group),
|
||||||
|
|||||||
+1
-1
@@ -1066,7 +1066,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||||||
|
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
WidgetHolder::unrelated_separator(),
|
WidgetHolder::unrelated_separator(),
|
||||||
DropdownInput::new(entries).selected_index(Some(sampling_method as u32)).tooltip("When selecing a layer in a folder, shallow select will select the parent folder whereas deep select will select the layer. Double clicking in shallow select mode will select the layer.").widget_holder(),
|
DropdownInput::new(entries).selected_index(Some(sampling_method as u32)).widget_holder(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Algorithm used to generate the image during each sampling step")
|
LayoutGroup::Row { widgets }.with_tooltip("Algorithm used to generate the image during each sampling step")
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ pub enum TransformOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TransformOperation {
|
impl TransformOperation {
|
||||||
pub fn apply_transform_operation(&self, selected: &mut Selected, snapping: bool) {
|
pub fn apply_transform_operation(&self, selected: &mut Selected, snapping: bool, axis_constraint: Axis) {
|
||||||
if self != &TransformOperation::None {
|
if self != &TransformOperation::None {
|
||||||
let transformation = match self {
|
let transformation = match self {
|
||||||
TransformOperation::Grabbing(translation) => DAffine2::from_translation(translation.to_dvec()),
|
TransformOperation::Grabbing(translation) => DAffine2::from_translation(translation.to_dvec()),
|
||||||
@@ -151,7 +151,7 @@ impl TransformOperation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
selected.update_transforms(transformation);
|
selected.update_transforms(transformation);
|
||||||
self.hints(snapping, selected.responses);
|
self.hints(snapping, axis_constraint, selected.responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ impl TransformOperation {
|
|||||||
TransformOperation::Scaling(scale) => scale.constraint.set_or_toggle(axis),
|
TransformOperation::Scaling(scale) => scale.constraint.set_or_toggle(axis),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.apply_transform_operation(selected, snapping);
|
self.apply_transform_operation(selected, snapping, axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_typed(&mut self, typed: Option<f64>, selected: &mut Selected, snapping: bool) {
|
pub fn handle_typed(&mut self, typed: Option<f64>, selected: &mut Selected, snapping: bool) {
|
||||||
@@ -174,20 +174,38 @@ impl TransformOperation {
|
|||||||
TransformOperation::Scaling(scale) => scale.typed_factor = typed,
|
TransformOperation::Scaling(scale) => scale.typed_factor = typed,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.apply_transform_operation(selected, snapping);
|
let axis_constraint = match self {
|
||||||
|
TransformOperation::Grabbing(grabbing) => grabbing.constraint,
|
||||||
|
TransformOperation::Scaling(scaling) => scaling.constraint,
|
||||||
|
_ => Axis::Both,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.apply_transform_operation(selected, snapping, axis_constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hints(&self, snapping: bool, responses: &mut VecDeque<Message>) {
|
pub fn hints(&self, snapping: bool, axis_constraint: Axis, responses: &mut VecDeque<Message>) {
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
|
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
let mut hints = Vec::new();
|
let mut hints = Vec::new();
|
||||||
|
|
||||||
|
let axis_str = |vector: DVec2, separate: bool| match axis_constraint {
|
||||||
|
Axis::Both => {
|
||||||
|
if separate {
|
||||||
|
format!("X: {}, Y: {}", vector.x, vector.y)
|
||||||
|
} else {
|
||||||
|
vector.x.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Axis::X => format!("X: {}", vector.x),
|
||||||
|
Axis::Y => format!("Y: {}", vector.y),
|
||||||
|
};
|
||||||
|
|
||||||
let value_str = match self {
|
let value_str = match self {
|
||||||
TransformOperation::None => String::new(),
|
TransformOperation::None => String::new(),
|
||||||
TransformOperation::Grabbing(translation) => format!("Translate X: {} Y: {}", translation.to_dvec().x, translation.to_dvec().y),
|
TransformOperation::Grabbing(translation) => format!("Translate {}", axis_str(translation.to_dvec(), true)),
|
||||||
TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping)),
|
TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping) * 360. / std::f64::consts::TAU),
|
||||||
TransformOperation::Scaling(scale) => format!("Scale X: {} Y: {}", scale.to_dvec(snapping).x, scale.to_dvec(snapping).y),
|
TransformOperation::Scaling(scale) => format!("Scale {}", axis_str(scale.to_dvec(snapping), false)),
|
||||||
};
|
};
|
||||||
hints.push(HintInfo::label(value_str));
|
hints.push(HintInfo::label(value_str));
|
||||||
hints.push(HintInfo::keys([Key::Shift], "Precision Mode"));
|
hints.push(HintInfo::keys([Key::Shift], "Precision Mode"));
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ use crate::messages::prelude::*;
|
|||||||
|
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use document_legacy::{LayerId, Operation};
|
use document_legacy::{LayerId, Operation};
|
||||||
use glam::DAffine2;
|
|
||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
|
|
||||||
|
use glam::DAffine2;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
/// Create a new vector layer from a vector of [`bezier_rs::Subpath`].
|
/// Create a new vector layer from a vector of [`bezier_rs::Subpath`].
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use super::shape_editor::SelectedShapeState;
|
||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::consts::VIEWPORT_GRID_ROUNDING_BIAS;
|
use crate::consts::VIEWPORT_GRID_ROUNDING_BIAS;
|
||||||
use crate::consts::{COLOR_ACCENT, HIDE_HANDLE_DISTANCE, MANIPULATOR_GROUP_MARKER_SIZE, PATH_OUTLINE_WEIGHT};
|
use crate::consts::{COLOR_ACCENT, HIDE_HANDLE_DISTANCE, MANIPULATOR_GROUP_MARKER_SIZE, PATH_OUTLINE_WEIGHT};
|
||||||
@@ -9,11 +10,9 @@ use document_legacy::layers::style::{self, Fill, Stroke};
|
|||||||
use document_legacy::{LayerId, Operation};
|
use document_legacy::{LayerId, Operation};
|
||||||
use graphene_core::raster::color::Color;
|
use graphene_core::raster::color::Color;
|
||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
|
||||||
use graphene_core::vector::{ManipulatorPointId, SelectedType};
|
use graphene_core::vector::{ManipulatorPointId, SelectedType};
|
||||||
|
|
||||||
use super::shape_editor::SelectedShapeState;
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
/// [ManipulatorGroupOverlay]s is the collection of overlays that make up an [ManipulatorGroup] visible in the editor.
|
/// [ManipulatorGroupOverlay]s is the collection of overlays that make up an [ManipulatorGroup] visible in the editor.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@@ -238,20 +237,25 @@ impl OverlayRenderer {
|
|||||||
/// Updates the position of the overlays based on the [Subpath] points.
|
/// Updates the position of the overlays based on the [Subpath] points.
|
||||||
fn place_manipulator_group_overlays(manipulator_group: &GraphiteManipulatorGroup, overlays: &mut ManipulatorGroupOverlays, parent_transform: &DAffine2, responses: &mut VecDeque<Message>) {
|
fn place_manipulator_group_overlays(manipulator_group: &GraphiteManipulatorGroup, overlays: &mut ManipulatorGroupOverlays, parent_transform: &DAffine2, responses: &mut VecDeque<Message>) {
|
||||||
let anchor = manipulator_group.anchor;
|
let anchor = manipulator_group.anchor;
|
||||||
|
|
||||||
let mut place_handle_and_line = |handle_position: DVec2, line_overlay: &[LayerId], marker_source: &mut Option<Vec<LayerId>>| {
|
let mut place_handle_and_line = |handle_position: DVec2, line_overlay: &[LayerId], marker_source: &mut Option<Vec<LayerId>>| {
|
||||||
let line_vector = parent_transform.transform_point2(anchor) - parent_transform.transform_point2(handle_position);
|
let line_vector = parent_transform.transform_point2(anchor) - parent_transform.transform_point2(handle_position);
|
||||||
let scale = DVec2::splat(line_vector.length());
|
let scale = DVec2::splat(line_vector.length());
|
||||||
let angle = -line_vector.angle_between(DVec2::X);
|
let angle = -line_vector.angle_between(DVec2::X);
|
||||||
|
|
||||||
let translation = (parent_transform.transform_point2(handle_position) + VIEWPORT_GRID_ROUNDING_BIAS).round() + DVec2::splat(0.5);
|
let translation = (parent_transform.transform_point2(handle_position) + VIEWPORT_GRID_ROUNDING_BIAS).round() + DVec2::splat(0.5);
|
||||||
let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
||||||
responses.push_back(Self::overlay_transform_message(line_overlay.to_vec(), transform));
|
responses.push_back(Self::overlay_transform_message(line_overlay.to_vec(), transform));
|
||||||
|
|
||||||
let marker_overlay = marker_source.take().unwrap_or_else(|| Self::create_handle_overlay(responses));
|
let marker_overlay = marker_source.take().unwrap_or_else(|| Self::create_handle_overlay(responses));
|
||||||
|
|
||||||
let scale = DVec2::splat(MANIPULATOR_GROUP_MARKER_SIZE);
|
let scale = DVec2::splat(MANIPULATOR_GROUP_MARKER_SIZE);
|
||||||
let angle = 0.;
|
let angle = 0.;
|
||||||
let translation = (parent_transform.transform_point2(handle_position) - (scale / 2.) + VIEWPORT_GRID_ROUNDING_BIAS).round();
|
let translation = (parent_transform.transform_point2(handle_position) - (scale / 2.) + VIEWPORT_GRID_ROUNDING_BIAS).round();
|
||||||
let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
||||||
|
|
||||||
responses.push_back(Self::overlay_transform_message(marker_overlay.clone(), transform));
|
responses.push_back(Self::overlay_transform_message(marker_overlay.clone(), transform));
|
||||||
|
|
||||||
*marker_source = Some(marker_overlay);
|
*marker_source = Some(marker_overlay);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -259,8 +263,8 @@ impl OverlayRenderer {
|
|||||||
if let (Some(handle_position), Some(line_overlay)) = (manipulator_group.in_handle, overlays.in_line.as_mut()) {
|
if let (Some(handle_position), Some(line_overlay)) = (manipulator_group.in_handle, overlays.in_line.as_mut()) {
|
||||||
place_handle_and_line(handle_position, line_overlay, &mut overlays.in_handle);
|
place_handle_and_line(handle_position, line_overlay, &mut overlays.in_handle);
|
||||||
}
|
}
|
||||||
if let (Some(handle_psoition), Some(line_overlay)) = (manipulator_group.out_handle, overlays.out_line.as_ref()) {
|
if let (Some(handle_position), Some(line_overlay)) = (manipulator_group.out_handle, overlays.out_line.as_ref()) {
|
||||||
place_handle_and_line(handle_psoition, line_overlay, &mut overlays.out_handle);
|
place_handle_and_line(handle_position, line_overlay, &mut overlays.out_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place the anchor point overlay
|
// Place the anchor point overlay
|
||||||
|
|||||||
@@ -179,14 +179,14 @@ impl ShapeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mirror_distance && point.manipulator_type != SelectedType::Anchor && vector_data.mirror_angle.contains(&point.group) {
|
if mirror_distance && point.manipulator_type != SelectedType::Anchor && vector_data.mirror_angle.contains(&point.group) {
|
||||||
let Some(mut origional_handle_position) = point.manipulator_type.get_position(group) else { continue };
|
let Some(mut original_handle_position) = point.manipulator_type.get_position(group) else { continue };
|
||||||
origional_handle_position += delta;
|
original_handle_position += delta;
|
||||||
|
|
||||||
let point = ManipulatorPointId::new(point.group, point.manipulator_type.opposite());
|
let point = ManipulatorPointId::new(point.group, point.manipulator_type.opposite());
|
||||||
if state.is_selected(point) {
|
if state.is_selected(point) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let position = group.anchor - (origional_handle_position - group.anchor);
|
let position = group.anchor - (original_handle_position - group.anchor);
|
||||||
responses.add(GraphOperationMessage::Vector {
|
responses.add(GraphOperationMessage::Vector {
|
||||||
layer: layer_path.clone(),
|
layer: layer_path.clone(),
|
||||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||||
@@ -363,8 +363,6 @@ impl ShapeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find the `t` value along the path segment we have clicked upon, together with that segment ID.
|
/// Find the `t` value along the path segment we have clicked upon, together with that segment ID.
|
||||||
///
|
|
||||||
/// Returns a tuple of subpath_index, manipulator_start and `t` as an f64.
|
|
||||||
fn closest_segment(&self, document: &Document, layer_path: &[LayerId], position: glam::DVec2, tolerance: f64) -> Option<(ManipulatorGroupId, ManipulatorGroupId, Bezier, f64)> {
|
fn closest_segment(&self, document: &Document, layer_path: &[LayerId], position: glam::DVec2, tolerance: f64) -> Option<(ManipulatorGroupId, ManipulatorGroupId, Bezier, f64)> {
|
||||||
let transform = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
let transform = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
||||||
let layer_pos = transform.inverse().transform_point2(position);
|
let layer_pos = transform.inverse().transform_point2(position);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
|
|||||||
if self.transform_layer_handler.is_transforming() {
|
if self.transform_layer_handler.is_transforming() {
|
||||||
self.transform_layer_handler.hints(responses);
|
self.transform_layer_handler.hints(responses);
|
||||||
} else {
|
} else {
|
||||||
tool.process_message(ToolMessage::UpdateHints, responses, &mut data)
|
tool.process_message(ToolMessage::UpdateHints, responses, &mut data);
|
||||||
}
|
}
|
||||||
tool.process_message(ToolMessage::UpdateCursor, responses, &mut data);
|
tool.process_message(ToolMessage::UpdateCursor, responses, &mut data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use crate::consts::SLOWING_DIVISOR;
|
|||||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||||
use crate::messages::portfolio::document::utility_types::transformation::{Axis, OriginalTransforms, Selected, TransformOperation, Typing};
|
use crate::messages::portfolio::document::utility_types::transformation::{Axis, OriginalTransforms, Selected, TransformOperation, Typing};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||||
use crate::messages::tool::utility_types::{ToolData, ToolType};
|
use crate::messages::tool::utility_types::{ToolData, ToolType};
|
||||||
|
|
||||||
@@ -29,7 +28,12 @@ impl TransformLayerMessageHandler {
|
|||||||
self.transform_operation != TransformOperation::None
|
self.transform_operation != TransformOperation::None
|
||||||
}
|
}
|
||||||
pub fn hints(&self, responses: &mut VecDeque<Message>) {
|
pub fn hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
self.transform_operation.hints(self.snap, responses);
|
let axis_constraint = match self.transform_operation {
|
||||||
|
TransformOperation::Grabbing(grabbing) => grabbing.constraint,
|
||||||
|
TransformOperation::Scaling(scaling) => scaling.constraint,
|
||||||
|
_ => Axis::Both,
|
||||||
|
};
|
||||||
|
self.transform_operation.hints(self.snap, axis_constraint, responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +119,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse);
|
begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse);
|
||||||
|
|
||||||
self.transform_operation = TransformOperation::Scaling(Default::default());
|
self.transform_operation = TransformOperation::Scaling(Default::default());
|
||||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
self.transform_operation.apply_transform_operation(&mut selected, self.snap, Axis::Both);
|
||||||
|
|
||||||
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||||
self.original_transforms.clear();
|
self.original_transforms.clear();
|
||||||
@@ -139,7 +143,12 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
let new_snap = ipp.keyboard.get(snap_key as usize);
|
let new_snap = ipp.keyboard.get(snap_key as usize);
|
||||||
if new_snap != self.snap {
|
if new_snap != self.snap {
|
||||||
self.snap = new_snap;
|
self.snap = new_snap;
|
||||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
let axis_constraint = match self.transform_operation {
|
||||||
|
TransformOperation::Grabbing(grabbing) => grabbing.constraint,
|
||||||
|
TransformOperation::Scaling(scaling) => scaling.constraint,
|
||||||
|
_ => Axis::Both,
|
||||||
|
};
|
||||||
|
self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.typing.digits.is_empty() {
|
if self.typing.digits.is_empty() {
|
||||||
@@ -149,8 +158,9 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
TransformOperation::None => unreachable!(),
|
TransformOperation::None => unreachable!(),
|
||||||
TransformOperation::Grabbing(translation) => {
|
TransformOperation::Grabbing(translation) => {
|
||||||
let change = if self.slow { delta_pos / SLOWING_DIVISOR } else { delta_pos };
|
let change = if self.slow { delta_pos / SLOWING_DIVISOR } else { delta_pos };
|
||||||
|
let axis_constraint = translation.constraint;
|
||||||
self.transform_operation = TransformOperation::Grabbing(translation.increment_amount(change));
|
self.transform_operation = TransformOperation::Grabbing(translation.increment_amount(change));
|
||||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint);
|
||||||
}
|
}
|
||||||
TransformOperation::Rotating(rotation) => {
|
TransformOperation::Rotating(rotation) => {
|
||||||
let selected_pivot = selected.mean_average_of_pivots(render_data);
|
let selected_pivot = selected.mean_average_of_pivots(render_data);
|
||||||
@@ -163,7 +173,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
|
|
||||||
let change = if self.slow { angle / SLOWING_DIVISOR } else { angle };
|
let change = if self.slow { angle / SLOWING_DIVISOR } else { angle };
|
||||||
self.transform_operation = TransformOperation::Rotating(rotation.increment_amount(change));
|
self.transform_operation = TransformOperation::Rotating(rotation.increment_amount(change));
|
||||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
self.transform_operation.apply_transform_operation(&mut selected, self.snap, Axis::Both);
|
||||||
}
|
}
|
||||||
TransformOperation::Scaling(scale) => {
|
TransformOperation::Scaling(scale) => {
|
||||||
let change = {
|
let change = {
|
||||||
@@ -175,8 +185,9 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
};
|
};
|
||||||
|
|
||||||
let change = if self.slow { change / SLOWING_DIVISOR } else { change };
|
let change = if self.slow { change / SLOWING_DIVISOR } else { change };
|
||||||
|
let axis_constraint = scale.constraint;
|
||||||
self.transform_operation = TransformOperation::Scaling(scale.increment_amount(change));
|
self.transform_operation = TransformOperation::Scaling(scale.increment_amount(change));
|
||||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ impl NodeGraphExecutor {
|
|||||||
}?;
|
}?;
|
||||||
let network = node_graph_frame.network.clone();
|
let network = node_graph_frame.network.clone();
|
||||||
|
|
||||||
// Special execution path for generating imaginate (as generation requires io from outside node graph)
|
// Special execution path for generating Imaginate (as generation requires IO from outside node graph)
|
||||||
if let Some(imaginate_node) = imaginate_node {
|
if let Some(imaginate_node) = imaginate_node {
|
||||||
responses.push_back(self.generate_imaginate(network, imaginate_node, (document, document_id), layer_path, image_frame, persistent_data)?);
|
responses.push_back(self.generate_imaginate(network, imaginate_node, (document, document_id), layer_path, image_frame, persistent_data)?);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ impl Bezier {
|
|||||||
let a = self.end.y - self.start.y;
|
let a = self.end.y - self.start.y;
|
||||||
let b = self.start.x - self.end.x;
|
let b = self.start.x - self.end.x;
|
||||||
let c = a * self.start.x + b * self.start.y;
|
let c = a * self.start.x + b * self.start.y;
|
||||||
if (a * target_point.x + b * target_point.y - c) * (resulting_sign as f64) <= 0.0 {
|
if (a * target_point.x + b * target_point.y - c) * (resulting_sign as f64) <= 0. {
|
||||||
resulting_sign
|
resulting_sign
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
@@ -448,14 +448,14 @@ impl Bezier {
|
|||||||
if target_point.x >= self.start.x.max(self.end.x).max(p1.x) {
|
if target_point.x >= self.start.x.max(self.end.x).max(p1.x) {
|
||||||
return resulting_sign;
|
return resulting_sign;
|
||||||
}
|
}
|
||||||
let a = self.end.y - 2.0 * p1.y + self.start.y;
|
let a = self.end.y - 2. * p1.y + self.start.y;
|
||||||
let b = 2.0 * (p1.y - self.start.y);
|
let b = 2. * (p1.y - self.start.y);
|
||||||
let c = self.start.y - target_point.y;
|
let c = self.start.y - target_point.y;
|
||||||
|
|
||||||
let discriminant = b * b - 4. * a * c;
|
let discriminant = b * b - 4. * a * c;
|
||||||
let two_times_a = 2. * a;
|
let two_times_a = 2. * a;
|
||||||
for t in solve_quadratic(discriminant, two_times_a, b, c) {
|
for t in solve_quadratic(discriminant, two_times_a, b, c) {
|
||||||
if (0.0..=1.0).contains(&t) {
|
if (0.0..=1.).contains(&t) {
|
||||||
let x = self.evaluate(TValue::Parametric(t)).x;
|
let x = self.evaluate(TValue::Parametric(t)).x;
|
||||||
if target_point.x >= x {
|
if target_point.x >= x {
|
||||||
return resulting_sign;
|
return resulting_sign;
|
||||||
@@ -473,12 +473,12 @@ impl Bezier {
|
|||||||
if target_point.x >= self.start.x.max(self.end.x).max(p1.x).max(p2.x) {
|
if target_point.x >= self.start.x.max(self.end.x).max(p1.x).max(p2.x) {
|
||||||
return resulting_sign;
|
return resulting_sign;
|
||||||
}
|
}
|
||||||
let a = self.end.y - 3.0 * p2.y + 3.0 * p1.y - self.start.y;
|
let a = self.end.y - 3. * p2.y + 3. * p1.y - self.start.y;
|
||||||
let b = 3.0 * (p2.y - 2.0 * p1.y + self.start.y);
|
let b = 3. * (p2.y - 2. * p1.y + self.start.y);
|
||||||
let c = 3.0 * (p1.y - self.start.y);
|
let c = 3. * (p1.y - self.start.y);
|
||||||
let d = self.start.y - target_point.y;
|
let d = self.start.y - target_point.y;
|
||||||
for t in solve_cubic(a, b, c, d) {
|
for t in solve_cubic(a, b, c, d) {
|
||||||
if (0.0..=1.0).contains(&t) {
|
if (0.0..=1.).contains(&t) {
|
||||||
let x = self.evaluate(TValue::Parametric(t)).x;
|
let x = self.evaluate(TValue::Parametric(t)).x;
|
||||||
if target_point.x >= x {
|
if target_point.x >= x {
|
||||||
return resulting_sign;
|
return resulting_sign;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||||||
self.closed
|
self.closed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set if the subpath is closed.
|
/// Set whether the subpath is closed.
|
||||||
pub fn set_closed(&mut self, new_closed: bool) {
|
pub fn set_closed(&mut self, new_closed: bool) {
|
||||||
self.closed = new_closed;
|
self.closed = new_closed;
|
||||||
}
|
}
|
||||||
@@ -29,12 +29,12 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||||||
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
|
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a manipulator group at an index
|
/// Insert a manipulator group at an index.
|
||||||
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<ManipulatorGroupId>) {
|
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<ManipulatorGroupId>) {
|
||||||
self.manipulator_groups.insert(index, group)
|
self.manipulator_groups.insert(index, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a manipulator group at an index
|
/// Remove a manipulator group at an index.
|
||||||
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<ManipulatorGroupId> {
|
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<ManipulatorGroupId> {
|
||||||
self.manipulator_groups.remove(index)
|
self.manipulator_groups.remove(index)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,4 +39,3 @@ node-macro = {path = "../node-macro"}
|
|||||||
specta.workspace = true
|
specta.workspace = true
|
||||||
specta.optional = true
|
specta.optional = true
|
||||||
once_cell = { version = "1.17.0", default-features = false, optional = true }
|
once_cell = { version = "1.17.0", default-features = false, optional = true }
|
||||||
# forma = { version = "0.1.0", package = "forma-render" }
|
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ fn unit_square(_input: ()) -> VectorData {
|
|||||||
super::VectorData::from_subpaths(vec![Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE)])
|
super::VectorData::from_subpaths(vec![Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE)])
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: I removed the Arc requirement we shouuld think about when it makes sense to use its
|
// TODO(TrueDoctor): I removed the Arc requirement we should think about when it makes sense to use it vs making a generic value node
|
||||||
// vs making a generic value node
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PathGenerator<Mirror> {
|
pub struct PathGenerator<Mirror> {
|
||||||
mirror: Mirror,
|
mirror: Mirror,
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use crate::uuid::ManipulatorGroupId;
|
|
||||||
|
|
||||||
use super::consts::ManipulatorType;
|
use super::consts::ManipulatorType;
|
||||||
use super::id_vec::IdBackedVec;
|
use super::id_vec::IdBackedVec;
|
||||||
use super::manipulator_group::ManipulatorGroup;
|
use super::manipulator_group::ManipulatorGroup;
|
||||||
use super::manipulator_point::ManipulatorPoint;
|
use super::manipulator_point::ManipulatorPoint;
|
||||||
|
use crate::uuid::ManipulatorGroupId;
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::{uuid::ManipulatorGroupId, Color};
|
|||||||
|
|
||||||
use bezier_rs::ManipulatorGroup;
|
use bezier_rs::ManipulatorGroup;
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
/// [VectorData] is passed between nodes.
|
/// [VectorData] is passed between nodes.
|
||||||
@@ -17,7 +18,7 @@ pub struct VectorData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VectorData {
|
impl VectorData {
|
||||||
/// An empty subpath with no data, an identity transform and a black fill.
|
/// An empty subpath with no data, an identity transform, and a black fill.
|
||||||
pub const fn empty() -> Self {
|
pub const fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
subpaths: Vec::new(),
|
subpaths: Vec::new(),
|
||||||
@@ -77,15 +78,15 @@ impl VectorData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the pivot of the layer in layerspace (the coordinates of the subpaths)
|
/// Compute the pivot of the layer in layerspace (the coordinates of the subpaths)
|
||||||
pub fn layerspace_pivot(&self, normalised_pivot: DVec2) -> DVec2 {
|
pub fn layerspace_pivot(&self, normalized_pivot: DVec2) -> DVec2 {
|
||||||
let [bounds_min, bounds_max] = self.nonzero_bounding_box();
|
let [bounds_min, bounds_max] = self.nonzero_bounding_box();
|
||||||
let bounds_size = bounds_max - bounds_min;
|
let bounds_size = bounds_max - bounds_min;
|
||||||
bounds_min + bounds_size * normalised_pivot
|
bounds_min + bounds_size * normalized_pivot
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the pivot in local space with the current transform applied
|
/// Compute the pivot in local space with the current transform applied
|
||||||
pub fn local_pivot(&self, normalised_pivot: DVec2) -> DVec2 {
|
pub fn local_pivot(&self, normalized_pivot: DVec2) -> DVec2 {
|
||||||
self.transform.transform_point2(self.layerspace_pivot(normalised_pivot))
|
self.transform.transform_point2(self.layerspace_pivot(normalized_pivot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user