-
Notifications
You must be signed in to change notification settings - Fork 323
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
Node searcher zoom & edited node growth/shrink animation #3327
Conversation
86ef3f5
to
5c41a50
Compare
5c41a50
to
43c2920
Compare
app/gui/view/graph-editor/src/lib.rs
Outdated
@@ -2596,6 +2612,59 @@ fn new_graph_editor(app: &Application) -> GraphEditor { | |||
} | |||
|
|||
|
|||
// === Edited node growth/shrink animation === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic should be outsourced to a separate file to avoid further growing the graph editor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, moved this FRP code to a separate module in component/node
hierarchy (where I feel it belongs).
app/gui/view/graph-editor/src/lib.rs
Outdated
@@ -1428,6 +1430,20 @@ impl GraphEditorModelWithNetwork { | |||
node_id | |||
} | |||
|
|||
/// Move node to the `edited_node` scene layer, so that it is rendered by the separate camera. | |||
pub fn move_node_to_edited_node_layer(&self, node_id: NodeId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this and the following method operate only on the nodes
, they might be better suited to live in the Nodes
struct directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods were pure helpers to avoid code repetition in FRP definition! We need access to GraphEditorModel
because we need to access Nodes
struct, and we can't implement growth/shrink animation inside component/node
module, because it is implemented by moving a single edited_node
camera, so this behavior can't be shared by all Node
s.
Now these functions are moved to a separate module.
Very nice results!!!! This PR contains some heavy code, I need more time to review it – will be reviewed tomorrow. |
CHANGELOG.md
Outdated
@@ -2,6 +2,7 @@ | |||
|
|||
#### Visual Environment | |||
|
|||
- [Node Searcher preserves its zoom factor.][3327] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs additional explanation. Thunk about changelog as something read by users. They should understand what happened after reading these notes. The link to the task is "additional technical explanation", but without seeing it, it should already be clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added additional explanation, please take a look.
fn set_layers(&self, layer: &Layer, text_layer: &Layer, action_bar_layer: &Layer) { | ||
layer.add_exclusive(&self.display_object); | ||
action_bar_layer.add_exclusive(&self.action_bar); | ||
self.output.set_label_layer(text_layer); | ||
self.input.set_label_layer(text_layer); | ||
self.profiling_label.set_label_layer(text_layer); | ||
self.comment.add_to_scene_layer(text_layer); | ||
} | ||
|
||
/// Move all sub-components to `edited_node` layer. | ||
/// | ||
/// A simple [`Layer::add_exclusive`] wouldn't work because text rendering in ensogl uses a | ||
/// separate layer management API. | ||
pub fn move_to_edited_node_layer(&self) { | ||
let scene = &self.app.display.default_scene; | ||
let layer = &scene.layers.edited_node; | ||
let text_layer = &scene.layers.edited_node_text; | ||
let action_bar_layer = &scene.layers.edited_node; | ||
self.set_layers(layer, text_layer, action_bar_layer); | ||
} | ||
|
||
/// Move all sub-components to `main` layer. | ||
/// | ||
/// A simple [`Layer::add_exclusive`] wouldn't work because text rendering in ensogl uses a | ||
/// separate layer management API. | ||
pub fn move_to_main_layer(&self) { | ||
let scene = &self.app.display.default_scene; | ||
let layer = &scene.layers.main; | ||
let text_layer = &scene.layers.label; | ||
let action_bar_layer = &scene.layers.above_nodes; | ||
self.set_layers(layer, text_layer, action_bar_layer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs additional explanation in the documentation why action_bar_layer
is handled in such a special way. This is not obvious from reading the code, so I'd be very thankful for making the docs a little bit more expanded here with clear explanation of which layer handles what in every scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
action_bar
is the only node component that uses a separate scene layer (above_nodes
). I'm not aware of the exact reasons behind that. I added a note to the docs about that.
let main_cam_frp = main_cam.frp(); | ||
|
||
|
||
// === Starting node editing. === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double space + we are not using dots in section titles - in English, section titles are dotless sentences. Which is kind of stupid, but it is this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
_eval <- all_with(&out.node_editing_started, &previous_edited_node, f!([model] (current, previous) { | ||
move_node_to_main_layer(&model, *previous); | ||
move_node_to_edited_node_layer(&model, *current); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like these functions that take model as argument, as they clearly belong to model. This can be rewritten to:
_eval <- all_with(&previous_edited_node, &out.node_editing_started, f!((previous, current) {
model.move_node_to_main_layer(previous);
model.move_node_to_edited_node_layer(current);
}));
I know that model is in another module, but you can just extend it with a local trait in this module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was written that way, but after moving to a separate module I changed methods to functions. Fixed it back now.
let weight = Vector3::from_element(*weight); | ||
let inv_weight = Vector3::from_element(1.0) - weight; | ||
target.component_mul(&weight) + animation.component_mul(&inv_weight) | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we format such lines this way instead, please?
edited_node_cam_position <- all_with3
(&edited_node_cam_target, &growth_animation.value, &animation_blending.value, |target,animation,weight| {
let weight = Vector3::from_element(*weight);
let inv_weight = Vector3::from_element(1.0) - weight;
target.component_mul(&weight) + animation.component_mul(&inv_weight)
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, done
app/gui/view/src/project.rs
Outdated
let move_to_edited_node = searcher_pos * (1.0 - zoom); | ||
preserve_zoom + move_to_edited_node | ||
}); | ||
eval searcher_cam_pos ([searcher_cam] (pos) searcher_cam.set_position_xy(*pos)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need this part here [searcher_cam]
. If the expression starts with foo.<something>
, then foo
will automatically be threaded as clone able thing by the f!
macro (and the eval
construct uses f!
under the hood).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know, fixed!
// on_end (inertia::EndStatus), | ||
// on_end (), | ||
// } | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an edit in a commented code. Is this on purpose? Why this code is commented in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this code. I suppose it lives here since prehistoric times.
/// Default animation precision. | ||
pub const DEFAULT_PRECISION: f32 = 0.001; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doc does not explain what it is at all. After reading it, I still dont understand what it does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added additional docs
set_value <- any_mut::<T>(); | ||
precision <- any_mut::<f32>(); | ||
skip <- any_mut::<()>(); | ||
eval target ((t) simulator.set_target_value(mix::into_space(t.clone()))); | ||
eval set_value ((t) simulator.set_value(mix::into_space(t.clone()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the idea for the ability to set the value of the physics-based animator. It can make the animations jumpy. It was not exposed on purpose. Can we have a fast call why this is needed and if there is a way not to expose it, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh well, actually it turned out that I'm not using it anywhere :) It was used in some previous versions of the code. Removed it
Pull Request Description
In this PR two things are implemented:
See attached video with different ways of editing/creating nodes:
2022-03-09.03-23-24.mp4
Technical details
node_searcher
,node_searcher_text
,edited_node
,edited_node_text
. Searcher is always rendered bynode_searcher
camera, edited node moves between its usual layers andedited_node
layer. Because text rendering uses different API, all node components were modified to support change of the layer.node_searcher
DOM layer, because documentation is implemented as a DOM object.ensogl::Animation
:on_end
andset_value
. These endpoints are useful while implementing growth/shrink animation.Camera2d
:position
andzoom
outputs. This allows to synchronize cameras easily using FRP networks.main
scene layer after editing is done.Important Notes
Checklist
Please include the following checklist in your PR:
./run dist
and./run watch
.