Differentiate between scale and dimensions (#570)
* Differentiate between scale and dimensions * Fix layout and naming of properties
This commit is contained in:
parent
9ced465150
commit
0ee492a857
|
|
@ -22,6 +22,8 @@ pub enum PropertiesPanelMessage {
|
||||||
pub enum TransformOp {
|
pub enum TransformOp {
|
||||||
X,
|
X,
|
||||||
Y,
|
Y,
|
||||||
|
ScaleX,
|
||||||
|
ScaleY,
|
||||||
Width,
|
Width,
|
||||||
Height,
|
Height,
|
||||||
Rotation,
|
Rotation,
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ use std::f64::consts::PI;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
trait DAffine2Utils {
|
trait DAffine2Utils {
|
||||||
fn width(&self) -> f64;
|
fn scale_x(&self) -> f64;
|
||||||
fn update_width(self, new_width: f64) -> Self;
|
fn update_scale_x(self, new_width: f64) -> Self;
|
||||||
fn height(&self) -> f64;
|
fn scale_y(&self) -> f64;
|
||||||
fn update_height(self, new_height: f64) -> Self;
|
fn update_scale_y(self, new_height: f64) -> Self;
|
||||||
fn x(&self) -> f64;
|
fn x(&self) -> f64;
|
||||||
fn update_x(self, new_x: f64) -> Self;
|
fn update_x(self, new_x: f64) -> Self;
|
||||||
fn y(&self) -> f64;
|
fn y(&self) -> f64;
|
||||||
|
|
@ -31,20 +31,20 @@ trait DAffine2Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DAffine2Utils for DAffine2 {
|
impl DAffine2Utils for DAffine2 {
|
||||||
fn width(&self) -> f64 {
|
fn scale_x(&self) -> f64 {
|
||||||
self.transform_vector2((1., 0.).into()).length()
|
self.transform_vector2((1., 0.).into()).length()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_width(self, new_width: f64) -> Self {
|
fn update_scale_x(self, new_width: f64) -> Self {
|
||||||
self * DAffine2::from_scale((new_width / self.width(), 1.).into())
|
self * DAffine2::from_scale((new_width / self.scale_x(), 1.).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn height(&self) -> f64 {
|
fn scale_y(&self) -> f64 {
|
||||||
self.transform_vector2((0., 1.).into()).length()
|
self.transform_vector2((0., 1.).into()).length()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_height(self, new_height: f64) -> Self {
|
fn update_scale_y(self, new_height: f64) -> Self {
|
||||||
self * DAffine2::from_scale((1., new_height / self.height()).into())
|
self * DAffine2::from_scale((1., new_height / self.scale_y()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x(&self) -> f64 {
|
fn x(&self) -> f64 {
|
||||||
|
|
@ -66,14 +66,14 @@ impl DAffine2Utils for DAffine2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotation(&self) -> f64 {
|
fn rotation(&self) -> f64 {
|
||||||
let cos = self.matrix2.col(0).x / self.width();
|
let cos = self.matrix2.col(0).x / self.scale_x();
|
||||||
let sin = self.matrix2.col(0).y / self.width();
|
let sin = self.matrix2.col(0).y / self.scale_x();
|
||||||
sin.atan2(cos)
|
sin.atan2(cos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_rotation(self, new_rotation: f64) -> Self {
|
fn update_rotation(self, new_rotation: f64) -> Self {
|
||||||
let width = self.width();
|
let width = self.scale_x();
|
||||||
let height = self.height();
|
let height = self.scale_y();
|
||||||
let half_width = width / 2.;
|
let half_width = width / 2.;
|
||||||
let half_height = height / 2.;
|
let half_height = height / 2.;
|
||||||
|
|
||||||
|
|
@ -138,15 +138,21 @@ impl MessageHandler<PropertiesPanelMessage, &GrapheneDocument> for PropertiesPan
|
||||||
let action = match transform_op {
|
let action = match transform_op {
|
||||||
X => DAffine2::update_x,
|
X => DAffine2::update_x,
|
||||||
Y => DAffine2::update_y,
|
Y => DAffine2::update_y,
|
||||||
Width => DAffine2::update_width,
|
ScaleX | Width => DAffine2::update_scale_x,
|
||||||
Height => DAffine2::update_height,
|
ScaleY | Height => DAffine2::update_scale_y,
|
||||||
Rotation => DAffine2::update_rotation,
|
Rotation => DAffine2::update_rotation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let scale = match transform_op {
|
||||||
|
Width => layer.bounding_transform().scale_x() / layer.transform.scale_x(),
|
||||||
|
Height => layer.bounding_transform().scale_y() / layer.transform.scale_y(),
|
||||||
|
_ => 1.,
|
||||||
|
};
|
||||||
|
|
||||||
responses.push_back(
|
responses.push_back(
|
||||||
Operation::SetLayerTransform {
|
Operation::SetLayerTransform {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
transform: action(layer.transform, value).to_cols_array(),
|
transform: action(layer.transform, value / scale).to_cols_array(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
@ -297,7 +303,7 @@ fn node_section_transform(layer: &Layer) -> LayoutRow {
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||||
value: "Position".into(),
|
value: "Location".into(),
|
||||||
..TextLabel::default()
|
..TextLabel::default()
|
||||||
})),
|
})),
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
WidgetHolder::new(Widget::Separator(Separator {
|
||||||
|
|
@ -336,6 +342,75 @@ fn node_section_transform(layer: &Layer) -> LayoutRow {
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
LayoutRow::Row {
|
||||||
|
name: "".into(),
|
||||||
|
widgets: vec![
|
||||||
|
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||||
|
value: "Rotation".into(),
|
||||||
|
..TextLabel::default()
|
||||||
|
})),
|
||||||
|
WidgetHolder::new(Widget::Separator(Separator {
|
||||||
|
separator_type: SeparatorType::Unrelated,
|
||||||
|
direction: SeparatorDirection::Horizontal,
|
||||||
|
})),
|
||||||
|
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||||
|
value: layer.transform.rotation() * 180. / PI,
|
||||||
|
label: "".into(),
|
||||||
|
unit: "°".into(),
|
||||||
|
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||||
|
PropertiesPanelMessage::ModifyTransform {
|
||||||
|
value: number_input.value / 180. * PI,
|
||||||
|
transform_op: TransformOp::Rotation,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}),
|
||||||
|
..NumberInput::default()
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
LayoutRow::Row {
|
||||||
|
name: "".into(),
|
||||||
|
widgets: vec![
|
||||||
|
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||||
|
value: "Scale".into(),
|
||||||
|
..TextLabel::default()
|
||||||
|
})),
|
||||||
|
WidgetHolder::new(Widget::Separator(Separator {
|
||||||
|
separator_type: SeparatorType::Unrelated,
|
||||||
|
direction: SeparatorDirection::Horizontal,
|
||||||
|
})),
|
||||||
|
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||||
|
value: layer.transform.scale_x(),
|
||||||
|
label: "X".into(),
|
||||||
|
unit: "".into(),
|
||||||
|
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||||
|
PropertiesPanelMessage::ModifyTransform {
|
||||||
|
value: number_input.value,
|
||||||
|
transform_op: TransformOp::ScaleX,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}),
|
||||||
|
..NumberInput::default()
|
||||||
|
})),
|
||||||
|
WidgetHolder::new(Widget::Separator(Separator {
|
||||||
|
separator_type: SeparatorType::Related,
|
||||||
|
direction: SeparatorDirection::Horizontal,
|
||||||
|
})),
|
||||||
|
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||||
|
value: layer.transform.scale_y(),
|
||||||
|
label: "Y".into(),
|
||||||
|
unit: "".into(),
|
||||||
|
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||||
|
PropertiesPanelMessage::ModifyTransform {
|
||||||
|
value: number_input.value,
|
||||||
|
transform_op: TransformOp::ScaleY,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}),
|
||||||
|
..NumberInput::default()
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
},
|
||||||
LayoutRow::Row {
|
LayoutRow::Row {
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
widgets: vec![
|
widgets: vec![
|
||||||
|
|
@ -348,7 +423,7 @@ fn node_section_transform(layer: &Layer) -> LayoutRow {
|
||||||
direction: SeparatorDirection::Horizontal,
|
direction: SeparatorDirection::Horizontal,
|
||||||
})),
|
})),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||||
value: layer.transform.width(),
|
value: layer.bounding_transform().scale_x(),
|
||||||
label: "W".into(),
|
label: "W".into(),
|
||||||
unit: " px".into(),
|
unit: " px".into(),
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||||
|
|
@ -365,7 +440,7 @@ fn node_section_transform(layer: &Layer) -> LayoutRow {
|
||||||
direction: SeparatorDirection::Horizontal,
|
direction: SeparatorDirection::Horizontal,
|
||||||
})),
|
})),
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||||
value: layer.transform.height(),
|
value: layer.bounding_transform().scale_y(),
|
||||||
label: "H".into(),
|
label: "H".into(),
|
||||||
unit: " px".into(),
|
unit: " px".into(),
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||||
|
|
@ -379,32 +454,6 @@ fn node_section_transform(layer: &Layer) -> LayoutRow {
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
LayoutRow::Row {
|
|
||||||
name: "".into(),
|
|
||||||
widgets: vec![
|
|
||||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
|
||||||
value: "Rotation".into(),
|
|
||||||
..TextLabel::default()
|
|
||||||
})),
|
|
||||||
WidgetHolder::new(Widget::Separator(Separator {
|
|
||||||
separator_type: SeparatorType::Unrelated,
|
|
||||||
direction: SeparatorDirection::Horizontal,
|
|
||||||
})),
|
|
||||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
|
||||||
value: layer.transform.rotation() * 180. / PI,
|
|
||||||
label: "R".into(),
|
|
||||||
unit: "°".into(),
|
|
||||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
|
||||||
PropertiesPanelMessage::ModifyTransform {
|
|
||||||
value: number_input.value / 180. * PI,
|
|
||||||
transform_op: TransformOp::Rotation,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}),
|
|
||||||
..NumberInput::default()
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ impl<'a> Selected<'a> {
|
||||||
.document
|
.document
|
||||||
.layer(path)
|
.layer(path)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.current_bounding_box_with_transform(multiplied_transform)
|
.aabounding_box_for_transform(multiplied_transform)
|
||||||
.unwrap_or([multiplied_transform.translation; 2]);
|
.unwrap_or([multiplied_transform.translation; 2]);
|
||||||
|
|
||||||
(bounds[0] + bounds[1]) / 2.
|
(bounds[0] + bounds[1]) / 2.
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ impl Default for GradientToolFsmState {
|
||||||
|
|
||||||
/// Computes the transform from gradient space to layer space (where gradient space is 0..1 in layer space)
|
/// Computes the transform from gradient space to layer space (where gradient space is 0..1 in layer space)
|
||||||
fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler) -> DAffine2 {
|
fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler) -> DAffine2 {
|
||||||
let bounds = layer.current_bounding_box().unwrap();
|
let bounds = layer.aabounding_box().unwrap();
|
||||||
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||||
|
|
||||||
document.graphene_document.multiply_transforms(&path[..path.len() - 1]).unwrap() * bound_transform
|
document.graphene_document.multiply_transforms(&path[..path.len() - 1]).unwrap() * bound_transform
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ fn update_overlays(document: &DocumentMessageHandler, data: &mut TextToolData, r
|
||||||
.graphene_document
|
.graphene_document
|
||||||
.layer(layer_path)
|
.layer(layer_path)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.current_bounding_box_with_transform(document.graphene_document.multiply_transforms(layer_path).unwrap())
|
.aabounding_box_for_transform(document.graphene_document.multiply_transforms(layer_path).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let operation = Operation::SetLayerTransformInViewport {
|
let operation = Operation::SetLayerTransformInViewport {
|
||||||
|
|
|
||||||
|
|
@ -146,12 +146,23 @@ impl Layer {
|
||||||
self.data.intersects_quad(transformed_quad, path, intersections)
|
self.data.intersects_quad(transformed_quad, path, intersections)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_bounding_box_with_transform(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
pub fn aabounding_box_for_transform(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||||
self.data.bounding_box(transform)
|
self.data.bounding_box(transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_bounding_box(&self) -> Option<[DVec2; 2]> {
|
pub fn aabounding_box(&self) -> Option<[DVec2; 2]> {
|
||||||
self.current_bounding_box_with_transform(self.transform)
|
self.aabounding_box_for_transform(self.transform)
|
||||||
|
}
|
||||||
|
pub fn bounding_transform(&self) -> DAffine2 {
|
||||||
|
let scale = match self.aabounding_box_for_transform(DAffine2::IDENTITY) {
|
||||||
|
Some([a, b]) => {
|
||||||
|
let dimensions = b - a;
|
||||||
|
DAffine2::from_scale(dimensions)
|
||||||
|
}
|
||||||
|
_ => DAffine2::IDENTITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.transform * scale
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_folder_mut(&mut self) -> Result<&mut FolderLayer, DocumentError> {
|
pub fn as_folder_mut(&mut self) -> Result<&mut FolderLayer, DocumentError> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue