Code review previous commit, and improve G/R/S hints
This commit is contained in:
parent
959e790cdf
commit
d6ab417bcb
|
|
@ -30,5 +30,4 @@ cargo install cargo-about
|
|||
# Build for production
|
||||
echo 👷 Build Graphite web client
|
||||
export NODE_ENV=production
|
||||
npm run build
|
||||
mv public dist
|
||||
npm run build && mv public dist # `&&` is used here to preserve the exit code
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use crate::messages::prelude::*;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::vector::ManipulatorPointId;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
pub type LayerIdentifier = Vec<document_legacy::LayerId>;
|
||||
|
||||
#[impl_message(Message, DocumentMessage, GraphOperation)]
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
use super::{resolve_document_node_type, VectorDataModification};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::{LayerId, Operation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork};
|
||||
use graphene_core::vector::style::{Fill, FillType, Stroke};
|
||||
use transform_utils::LayerBounds;
|
||||
|
||||
use super::{resolve_document_node_type, VectorDataModification};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
mod transform_utils;
|
||||
|
||||
|
|
@ -37,7 +37,8 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
let document_node = self.network.nodes.get_mut(&node_id).unwrap();
|
||||
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>)) {
|
||||
let output_node_id = self.network.outputs[0].node_id;
|
||||
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();
|
||||
self.responses.add(DocumentMessage::NodeGraphFrameGenerate { layer_path });
|
||||
}
|
||||
|
||||
fn fill_set(&mut self, fill: Fill) {
|
||||
self.modify_inputs("Fill", |inputs| {
|
||||
let fill_type = match fill {
|
||||
|
|
@ -94,6 +96,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn stroke_set(&mut self, stroke: Stroke) {
|
||||
self.modify_inputs("Stroke", |inputs| {
|
||||
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::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;
|
||||
transform_utils::update_transform(inputs, transform);
|
||||
});
|
||||
}
|
||||
|
||||
fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds) {
|
||||
self.modify_inputs("Transform", |inputs| {
|
||||
let to = match transform_in {
|
||||
|
|
@ -126,15 +130,16 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
TransformIn::Scope { scope } => scope * 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;
|
||||
transform_utils::update_transform(inputs, transform);
|
||||
});
|
||||
}
|
||||
|
||||
fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) {
|
||||
self.modify_inputs("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 transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
|
||||
transform_utils::update_transform(inputs, transform);
|
||||
|
|
@ -172,10 +177,10 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
});
|
||||
self.modify_inputs("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 new_layerspace_pivot = (new_bounds_max - new_bounds_min) * normalised_pivot + new_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) * normalized_pivot + new_bounds_min;
|
||||
let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot);
|
||||
let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use graph_craft::document::{value::TaggedValue, NodeInput};
|
|||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
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) {
|
||||
let x_axis = transform.matrix2.x_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
|
||||
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[2] = NodeInput::value(TaggedValue::F64(angle), 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 bounds: [DVec2; 2],
|
||||
pub bounds_transform: DAffine2,
|
||||
|
|
@ -64,11 +64,13 @@ impl LayerBounds {
|
|||
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 {
|
||||
DVec2::ZERO
|
||||
};
|
||||
|
||||
let angle = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(angle),
|
||||
..
|
||||
|
|
@ -92,6 +95,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||
} else {
|
||||
0.
|
||||
};
|
||||
|
||||
let scale = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(scale),
|
||||
..
|
||||
|
|
@ -101,6 +105,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||
} else {
|
||||
DVec2::ONE
|
||||
};
|
||||
|
||||
let shear = if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(shear),
|
||||
..
|
||||
|
|
@ -110,11 +115,12 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
|
|||
} else {
|
||||
DVec2::ZERO
|
||||
};
|
||||
|
||||
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
|
||||
pub fn get_current_normalised_pivot(inputs: &[NodeInput]) -> DVec2 {
|
||||
/// Extract the current normalized pivot from the layer
|
||||
pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(pivot),
|
||||
..
|
||||
|
|
@ -126,12 +132,16 @@ pub fn get_current_normalised_pivot(inputs: &[NodeInput]) -> DVec2 {
|
|||
}
|
||||
}
|
||||
|
||||
/// 
|
||||
///
|
||||
// \begin{bmatrix}
|
||||
// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\
|
||||
// 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}
|
||||
/// Source:
|
||||
/// ```tex
|
||||
/// \begin{bmatrix}
|
||||
/// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\
|
||||
/// 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]
|
||||
fn derive_transform() {
|
||||
for shear_x in -10..=10 {
|
||||
|
|
@ -147,19 +157,19 @@ fn derive_transform() {
|
|||
let scale = DVec2::new(scale_x, scale_y);
|
||||
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() * shear.x - scale.y * angle.sin(), scale.x * angle.sin() * shear.x + scale.y * angle.cos()),
|
||||
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.]);
|
||||
|
||||
assert!(
|
||||
new_transform.abs_diff_eq(origional_transform, 1e-10),
|
||||
"origional_transform {} new_transform {} / scale {} new_scale {} / angle {} new_angle {} / shear {} / new_shear {}",
|
||||
origional_transform,
|
||||
new_transform.abs_diff_eq(original_transform, 1e-10),
|
||||
"original_transform {} new_transform {} / scale {} new_scale {} / angle {} new_angle {} / shear {} / new_shear {}",
|
||||
original_transform,
|
||||
new_transform,
|
||||
scale,
|
||||
new_scale,
|
||||
|
|
@ -194,7 +204,7 @@ fn subpath_bounds(subpaths: &[Subpath<ManipulatorGroupId>]) -> [DVec2; 2] {
|
|||
.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] {
|
||||
let [bounds_min, bounds_max] = subpath_bounds(subpaths);
|
||||
clamp_bounds(bounds_min, bounds_max)
|
||||
|
|
@ -213,6 +223,7 @@ impl<'a> VectorModificationState<'a> {
|
|||
let subpath = &mut self.subpaths[subpath_index];
|
||||
subpath.insert_manipulator_group(subpath.len(), manipulator_group)
|
||||
}
|
||||
|
||||
fn insert(&mut self, manipulator_group: ManipulatorGroup<ManipulatorGroupId>, after_id: ManipulatorGroupId) {
|
||||
for subpath in self.subpaths.iter_mut() {
|
||||
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) {
|
||||
for subpath in self.subpaths.iter_mut() {
|
||||
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) {
|
||||
for subpath in self.subpaths.iter_mut() {
|
||||
if point.manipulator_type == SelectedType::Anchor {
|
||||
|
|
@ -245,6 +258,7 @@ impl<'a> VectorModificationState<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mirror(&mut self, id: ManipulatorGroupId, mirror_angle: bool) {
|
||||
if !mirror_angle {
|
||||
self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id);
|
||||
|
|
@ -252,6 +266,7 @@ impl<'a> VectorModificationState<'a> {
|
|||
self.mirror_angle_groups.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_mirror(&mut self, id: ManipulatorGroupId) {
|
||||
if self.mirror_angle_groups.contains(&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);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_position(&mut self, point: ManipulatorPointId, position: DVec2) {
|
||||
for subpath in self.subpaths.iter_mut() {
|
||||
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) {
|
||||
match modification {
|
||||
VectorDataModification::AddEndManipulatorGroup { subpath_index, manipulator_group } => self.insert_end(subpath_index, manipulator_group),
|
||||
|
|
|
|||
|
|
@ -1066,7 +1066,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
|
||||
widgets.extend_from_slice(&[
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ pub enum 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 {
|
||||
let transformation = match self {
|
||||
TransformOperation::Grabbing(translation) => DAffine2::from_translation(translation.to_dvec()),
|
||||
|
|
@ -151,7 +151,7 @@ impl TransformOperation {
|
|||
};
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
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) {
|
||||
|
|
@ -174,20 +174,38 @@ impl TransformOperation {
|
|||
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::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
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 {
|
||||
TransformOperation::None => String::new(),
|
||||
TransformOperation::Grabbing(translation) => format!("Translate X: {} Y: {}", translation.to_dvec().x, translation.to_dvec().y),
|
||||
TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping)),
|
||||
TransformOperation::Scaling(scale) => format!("Scale X: {} Y: {}", scale.to_dvec(snapping).x, scale.to_dvec(snapping).y),
|
||||
TransformOperation::Grabbing(translation) => format!("Translate {}", axis_str(translation.to_dvec(), true)),
|
||||
TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping) * 360. / std::f64::consts::TAU),
|
||||
TransformOperation::Scaling(scale) => format!("Scale {}", axis_str(scale.to_dvec(snapping), false)),
|
||||
};
|
||||
hints.push(HintInfo::label(value_str));
|
||||
hints.push(HintInfo::keys([Key::Shift], "Precision Mode"));
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ use crate::messages::prelude::*;
|
|||
|
||||
use bezier_rs::Subpath;
|
||||
use document_legacy::{LayerId, Operation};
|
||||
use glam::DAffine2;
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
|
||||
use glam::DAffine2;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// 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::consts::VIEWPORT_GRID_ROUNDING_BIAS;
|
||||
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 graphene_core::raster::color::Color;
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
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.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
|
@ -238,20 +237,25 @@ impl OverlayRenderer {
|
|||
/// 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>) {
|
||||
let anchor = manipulator_group.anchor;
|
||||
|
||||
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 scale = DVec2::splat(line_vector.length());
|
||||
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 transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
||||
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 scale = DVec2::splat(MANIPULATOR_GROUP_MARKER_SIZE);
|
||||
let angle = 0.;
|
||||
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();
|
||||
|
||||
responses.push_back(Self::overlay_transform_message(marker_overlay.clone(), transform));
|
||||
|
||||
*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()) {
|
||||
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()) {
|
||||
place_handle_and_line(handle_psoition, line_overlay, &mut overlays.out_handle);
|
||||
if let (Some(handle_position), Some(line_overlay)) = (manipulator_group.out_handle, overlays.out_line.as_ref()) {
|
||||
place_handle_and_line(handle_position, line_overlay, &mut overlays.out_handle);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
let Some(mut origional_handle_position) = point.manipulator_type.get_position(group) else { continue };
|
||||
origional_handle_position += delta;
|
||||
let Some(mut original_handle_position) = point.manipulator_type.get_position(group) else { continue };
|
||||
original_handle_position += delta;
|
||||
|
||||
let point = ManipulatorPointId::new(point.group, point.manipulator_type.opposite());
|
||||
if state.is_selected(point) {
|
||||
continue;
|
||||
}
|
||||
let position = group.anchor - (origional_handle_position - group.anchor);
|
||||
let position = group.anchor - (original_handle_position - group.anchor);
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
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.
|
||||
///
|
||||
/// 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)> {
|
||||
let transform = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
||||
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() {
|
||||
self.transform_layer_handler.hints(responses);
|
||||
} 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use crate::consts::SLOWING_DIVISOR;
|
|||
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::prelude::*;
|
||||
|
||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||
use crate::messages::tool::utility_types::{ToolData, ToolType};
|
||||
|
||||
|
|
@ -29,7 +28,12 @@ impl TransformLayerMessageHandler {
|
|||
self.transform_operation != TransformOperation::None
|
||||
}
|
||||
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);
|
||||
|
||||
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());
|
||||
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);
|
||||
if new_snap != self.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() {
|
||||
|
|
@ -149,8 +158,9 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||
TransformOperation::None => unreachable!(),
|
||||
TransformOperation::Grabbing(translation) => {
|
||||
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.apply_transform_operation(&mut selected, self.snap);
|
||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint);
|
||||
}
|
||||
TransformOperation::Rotating(rotation) => {
|
||||
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 };
|
||||
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) => {
|
||||
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 axis_constraint = scale.constraint;
|
||||
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();
|
||||
|
||||
// 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 {
|
||||
responses.push_back(self.generate_imaginate(network, imaginate_node, (document, document_id), layer_path, image_frame, persistent_data)?);
|
||||
return Ok(());
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ impl Bezier {
|
|||
let a = self.end.y - self.start.y;
|
||||
let b = self.start.x - self.end.x;
|
||||
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
|
||||
} else {
|
||||
0
|
||||
|
|
@ -448,14 +448,14 @@ impl Bezier {
|
|||
if target_point.x >= self.start.x.max(self.end.x).max(p1.x) {
|
||||
return resulting_sign;
|
||||
}
|
||||
let a = self.end.y - 2.0 * p1.y + self.start.y;
|
||||
let b = 2.0 * (p1.y - self.start.y);
|
||||
let a = self.end.y - 2. * p1.y + self.start.y;
|
||||
let b = 2. * (p1.y - self.start.y);
|
||||
let c = self.start.y - target_point.y;
|
||||
|
||||
let discriminant = b * b - 4. * a * c;
|
||||
let two_times_a = 2. * a;
|
||||
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;
|
||||
if target_point.x >= x {
|
||||
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) {
|
||||
return resulting_sign;
|
||||
}
|
||||
let a = self.end.y - 3.0 * p2.y + 3.0 * p1.y - self.start.y;
|
||||
let b = 3.0 * (p2.y - 2.0 * p1.y + self.start.y);
|
||||
let c = 3.0 * (p1.y - self.start.y);
|
||||
let a = self.end.y - 3. * p2.y + 3. * p1.y - self.start.y;
|
||||
let b = 3. * (p2.y - 2. * p1.y + self.start.y);
|
||||
let c = 3. * (p1.y - self.start.y);
|
||||
let d = self.start.y - target_point.y;
|
||||
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;
|
||||
if target_point.x >= x {
|
||||
return resulting_sign;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
|||
self.closed
|
||||
}
|
||||
|
||||
/// Set if the subpath is closed.
|
||||
/// Set whether the subpath is closed.
|
||||
pub fn set_closed(&mut self, new_closed: bool) {
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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>) {
|
||||
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> {
|
||||
self.manipulator_groups.remove(index)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,4 +39,3 @@ node-macro = {path = "../node-macro"}
|
|||
specta.workspace = true
|
||||
specta.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)])
|
||||
}
|
||||
|
||||
// TODO: I removed the Arc requirement we shouuld think about when it makes sense to use its
|
||||
// vs making a generic value node
|
||||
// TODO(TrueDoctor): I removed the Arc requirement we should think about when it makes sense to use it vs making a generic value node
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PathGenerator<Mirror> {
|
||||
mirror: Mirror,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use crate::uuid::ManipulatorGroupId;
|
||||
|
||||
use super::consts::ManipulatorType;
|
||||
use super::id_vec::IdBackedVec;
|
||||
use super::manipulator_group::ManipulatorGroup;
|
||||
use super::manipulator_point::ManipulatorPoint;
|
||||
use crate::uuid::ManipulatorGroupId;
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use crate::{uuid::ManipulatorGroupId, Color};
|
|||
|
||||
use bezier_rs::ManipulatorGroup;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
/// [VectorData] is passed between nodes.
|
||||
|
|
@ -17,7 +18,7 @@ pub struct 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 {
|
||||
Self {
|
||||
subpaths: Vec::new(),
|
||||
|
|
@ -77,15 +78,15 @@ impl VectorData {
|
|||
}
|
||||
|
||||
/// 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_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
|
||||
pub fn local_pivot(&self, normalised_pivot: DVec2) -> DVec2 {
|
||||
self.transform.transform_point2(self.layerspace_pivot(normalised_pivot))
|
||||
pub fn local_pivot(&self, normalized_pivot: DVec2) -> DVec2 {
|
||||
self.transform.transform_point2(self.layerspace_pivot(normalized_pivot))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue