Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign transforms #1558

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -423,20 +423,14 @@ impl<'a> ModifyInputsContext<'a> {
{
transform *= upstream_transform.inverse();
}
let final_transform = pivot.inverse() * to.inverse() * transform * pivot;
let final_transform = /*pivot.inverse() **/ to.inverse() * transform /* pivot*/;
transform_utils::update_transform(inputs, final_transform);
});
}

fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) {
fn pivot_set(&mut self, new_pivot: DVec2) {
self.modify_inputs("Transform", false, |inputs, node_id, metadata| {
let layer_transform = transform_utils::get_current_transform(inputs);
let upstream_transform = metadata.upstream_transform(node_id);
let old_pivot_transform = DAffine2::from_translation(upstream_transform.transform_point2(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))));
let new_pivot_transform = DAffine2::from_translation(upstream_transform.transform_point2(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);
inputs[5] = NodeInput::value(TaggedValue::DVec2(new_pivot), false);
transform_utils::set_pivot(inputs, new_pivot);
});
}

Expand Down Expand Up @@ -733,9 +727,8 @@ impl MessageHandler<GraphOperationMessage, GraphOperationHandlerData<'_>> for Gr
}
}
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.pivot_set(pivot, bounds);
modify_inputs.pivot_set(pivot);
}
}
GraphOperationMessage::Vector { layer, modification } => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,62 +71,90 @@ impl LayerBounds {

/// Get the current affine transform from the transform node's inputs
pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 {
let translation = if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(translation),
let translation = get_current_translation(inputs);
let angle = get_current_rotation(inputs);
let scale = get_current_scale(inputs);

let shear = if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(shear),
..
} = inputs[1]
} = inputs[4]
{
translation
shear
} else {
DVec2::ZERO
};

let angle = if let NodeInput::Value {
DAffine2::from_scale_angle_translation(scale, angle, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.])
}

/// 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),
..
} = inputs[5]
{
pivot
} else {
DVec2::splat(0.5)
}
}

pub fn get_current_rotation(inputs: &[NodeInput]) -> f64 {
if let NodeInput::Value {
tagged_value: TaggedValue::F64(angle),
..
} = inputs[2]
{
angle
} else {
0.
};
}
}

let scale = if let NodeInput::Value {
pub fn get_current_scale(inputs: &[NodeInput]) -> DVec2 {
if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(scale),
..
} = inputs[3]
{
scale
} else {
DVec2::ONE
};

let shear = if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(shear),
..
} = inputs[4]
{
shear
} 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 normalized pivot from the layer
pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
pub fn get_current_translation(inputs: &[NodeInput]) -> DVec2 {
if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(pivot),
tagged_value: TaggedValue::DVec2(translation),
..
} = inputs[5]
} = inputs[1]
{
pivot
translation
} else {
DVec2::splat(0.5)
DVec2::ZERO
}
}

pub fn set_pivot(inputs: &mut [NodeInput], new_pivot: DVec2) {
let old_pivot = get_current_normalized_pivot(inputs);
let rotation = get_current_rotation(inputs);
let scale = get_current_scale(inputs);
let translation = get_current_translation(inputs);

let pivot_diff = new_pivot - old_pivot;
let translation_offset = translation - pivot_diff;
let scale_pivot = scale * pivot_diff;
let cosr = rotation.cos() as f64;
let sinr = rotation.sin() as f64;
let scale_rotation_offset = DVec2::new(scale_pivot.x * cosr - scale_pivot.y * sinr, scale_pivot.x * sinr + scale_pivot.y * cosr);
let new_translation = scale_rotation_offset + translation_offset;

inputs[1] = NodeInput::value(TaggedValue::DVec2(new_translation), false);
inputs[5] = NodeInput::value(TaggedValue::DVec2(new_pivot), false);
}

/// ![](https://files.keavon.com/-/OptimisticSpotlessTinamou/capture.png)
///
/// Source:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,42 +138,13 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name

assist(&mut widgets);

if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(dvec2),
exposed: false,
} = document_node.inputs[index]
{
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(dvec2.x))
.label(x)
.unit(unit)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
.on_commit(commit_value)
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(dvec2.y))
.label(y)
.unit(unit)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
.on_commit(commit_value)
.widget_holder(),
]);
} else if let NodeInput::Value {
tagged_value: TaggedValue::IVec2(ivec2),
exposed: false,
} = document_node.inputs[index]
{
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, ivec2.y));
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(ivec2.x, input.value.unwrap() as i32));
let mut create_widget = |vec2: DVec2, to_tagged_value: &'static (dyn Fn(f64, f64) -> TaggedValue + Send + Sync)| {
let update_x = move |input: &NumberInput| to_tagged_value(input.value.unwrap(), vec2.y);
let update_y = move |input: &NumberInput| to_tagged_value(vec2.x, input.value.unwrap());

widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(ivec2.x as f64))
.int()
NumberInput::new(Some(vec2.x))
.label(x)
.unit(unit)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
Expand All @@ -182,8 +153,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
.on_commit(commit_value)
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(ivec2.y as f64))
.int()
NumberInput::new(Some(vec2.y))
.label(y)
.unit(unit)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
Expand All @@ -192,36 +162,18 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
.on_commit(commit_value)
.widget_holder(),
]);
} else if let NodeInput::Value {
tagged_value: TaggedValue::UVec2(uvec2),
exposed: false,
} = document_node.inputs[index]
{
let update_x = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(input.value.unwrap() as u32, uvec2.y));
let update_y = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(uvec2.x, input.value.unwrap() as u32));
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(uvec2.x as f64))
.int()
.label(x)
.unit(unit)
.min(min.unwrap_or(0.))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(update_x, node_id, index))
.on_commit(commit_value)
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(uvec2.y as f64))
.int()
.label(y)
.unit(unit)
.min(min.unwrap_or(0.))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(update_y, node_id, index))
.on_commit(commit_value)
.widget_holder(),
]);
}
};

let NodeInput::Value { ref tagged_value, exposed: false } = document_node.inputs[index] else {
return LayoutGroup::Row { widgets };
};

match tagged_value {
TaggedValue::DVec2(dvec2) => create_widget(*dvec2, &|x, y| TaggedValue::DVec2(DVec2::new(x, y))),
TaggedValue::IVec2(ivec2) => create_widget(ivec2.as_dvec2(), &|x, y| TaggedValue::IVec2(IVec2::new(x as i32, y as i32))),
TaggedValue::UVec2(uvec2) => create_widget(uvec2.as_dvec2(), &|x, y| TaggedValue::UVec2(UVec2::new(x as u32, y as u32))),
_ => (),
};

LayoutGroup::Row { widgets }
}
Expand Down Expand Up @@ -1551,16 +1503,24 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
{
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(
PivotInput::new(pivot.into())
.on_update(update_value(|pivot: &PivotInput| TaggedValue::DVec2(Into::<Option<DVec2>>::into(pivot.position).unwrap()), node_id, 5))
.on_commit(commit_value)
PivotInput::new(PivotPosition::Center.into())
.on_update(
|_| Message::NoOp, // update_value(
// |pivot: &PivotInput| TaggedValue::DVec2(Into::<Option<DVec2>>::into(pivot.position).unwrap()),
// node_id,
// pivot_index,
// )
)
.widget_holder(),
);
} else {
add_blank_assist(widgets);
}
};
let translation = vec2_widget(document_node, node_id, 1, "Translation", "X", "Y", " px", None, translation_assist);
// The Pivot assist would need to modify the values of the Translation which is a bit annoying to pull of
// so we'll disable this until we have the proper graphical entry method
// let pivot = vec2_widget(document_node, node_id, 5, "Pivot", "X", "Y", "", None, add_blank_assist);

let rotation = {
let index = 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,17 @@ impl DocumentMetadata {

/// Get the bounding box of the click target of the specified layer in the specified transform space
pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> {
self.click_targets
// log::debug!("click_target: {:?}", self.click_targets.get(&layer));

let bbox = self
.click_targets
.get(&layer)?
.iter()
.filter_map(|click_target| click_target.subpath.bounding_box_with_transform(transform))
.reduce(Quad::combine_bounds)
.reduce(Quad::combine_bounds);

// log::debug!("bounding_boux: {:?}", bbox);
bbox
}

/// Calculate the corners of the bounding box but with a nonzero size.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,7 @@ impl<'a> Selected<'a> {
.selected
.iter()
.map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.document_network, self.document_metadata))
.reduce(|a, b| a + b)
.unwrap_or_default();
.sum::<DVec2>();

xy_summation / self.selected.len() as f64
}
Expand All @@ -367,14 +366,24 @@ impl<'a> Selected<'a> {
(min + max) / 2.
}

fn transform_layer(document_metadata: &DocumentMetadata, layer: LayerNodeIdentifier, original_transform: Option<&DAffine2>, transformation: DAffine2, responses: &mut VecDeque<Message>) {
fn transform_layer(
pivot: DVec2,
document_metadata: &DocumentMetadata,
layer: LayerNodeIdentifier,
original_transform: Option<&DAffine2>,
transformation: DAffine2,
responses: &mut VecDeque<Message>,
) {
let Some(&original_transform) = original_transform else { return };
let to = document_metadata.downstream_transform_to_viewport(layer);
let new = to.inverse() * transformation * to * original_transform;
responses.add(GraphOperationMessage::TransformSet {
log::warn!("pivot: {:?}", pivot);
log::debug!("to: {to:?} transformation: {transformation:?}, original_transform: {original_transform:?}");
let new = transformation;
responses.add(GraphOperationMessage::TransformSetPivot { layer: layer.clone(), pivot });
responses.add(GraphOperationMessage::TransformChange {
layer,
transform: new,
transform_in: TransformIn::Local,
transform_in: TransformIn::Viewport,
skip_rerender: false,
});
}
Expand Down Expand Up @@ -407,20 +416,24 @@ impl<'a> Selected<'a> {
}

pub fn apply_transformation(&mut self, transformation: DAffine2) {
log::debug!("applying tranform: {transformation:?}");
if !self.selected.is_empty() {
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
for layer_ancestors in self.document_metadata.shallowest_unique_layers(self.selected.iter().copied()) {
let layer = *layer_ancestors.last().unwrap();

log::debug!("layer_arcestors: {layer_ancestors:?}");

match &self.original_transforms {
OriginalTransforms::Layer(layer_transforms) => Self::transform_layer(self.document_metadata, layer, layer_transforms.get(&layer), transformation, self.responses),
OriginalTransforms::Path(path_transforms) => Self::transform_path(self.document_metadata, layer, path_transforms.get(&layer), transformation, self.responses),
OriginalTransforms::Layer(layer_transforms) => Self::transform_layer(*self.pivot, self.document_metadata, layer, layer_transforms.get(&layer), transformation, self.responses),
OriginalTransforms::Path(path_transforms) => Self::transform_path(&self.document_metadata, layer, path_transforms.get(&layer), transformation, self.responses),
}
}
}
}

pub fn update_transforms(&mut self, delta: DAffine2) {
log::debug!("update_transforms: pivot: {:?}", self.pivot);
let pivot = DAffine2::from_translation(*self.pivot);
let transformation = pivot * delta * pivot.inverse();
self.apply_transformation(transformation);
Expand Down