Code review previous commit, and improve G/R/S hints

This commit is contained in:
Keavon Chambers 2023-03-26 11:49:25 -07:00
parent 959e790cdf
commit d6ab417bcb
18 changed files with 135 additions and 83 deletions

View File

@ -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

View File

@ -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)]

View File

@ -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);

View File

@ -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 {
}
}
/// ![](https://files.keavon.com/-/OptimisticSpotlessTinamou/capture.png)
///
// \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),

View File

@ -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")

View File

@ -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"));

View File

@ -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`].

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}
};
}

View File

@ -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(());

View File

@ -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;

View File

@ -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)
}

View File

@ -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" }

View File

@ -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,

View File

@ -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;

View File

@ -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))
}
}