Avoid per-frame mesh copies in both timeline layouts (#1907)
Thanks @crono2250.
This commit is contained in:
1 parent
932efbf341
commit
8f768b65c8
2 files changed
+61
-19
No files matched your search
@@ -4,13 +4,39 @@ pub(crate) use crate::icons::glyphs::transport_glyph;
|
|||||||
use egui::{Rect, Sense, Stroke, UiBuilder, pos2, vec2};
|
use egui::{Rect, Sense, Stroke, UiBuilder, pos2, vec2};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use solidcraft_engine::doc::{
|
use solidcraft_engine::doc::{
|
||||||
FeatureKind, PlaneRef,
|
FeatureKind, PlaneRef, SketchId,
|
||||||
construct::{AxisDef, PointDef},
|
construct::{AxisDef, PointDef},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::theme::Tokens;
|
use crate::theme::Tokens;
|
||||||
use crate::{SolveApp, icons};
|
use crate::{SolveApp, icons};
|
||||||
|
|
||||||
|
// Snapshot only the data used by timeline interactions. Cloning FeatureKind here would
|
||||||
|
// also copy imported mesh vertices and triangles on every frame.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) enum TimelineKind {
|
||||||
|
Sketch,
|
||||||
|
Profile(SketchId),
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimelineKind {
|
||||||
|
pub(crate) fn of(kind: &FeatureKind) -> Self {
|
||||||
|
match kind {
|
||||||
|
FeatureKind::Sketch { .. } => Self::Sketch,
|
||||||
|
FeatureKind::Extrude { sketch, .. } | FeatureKind::Revolve { sketch, .. } | FeatureKind::Sweep { sketch, .. } => Self::Profile(*sketch),
|
||||||
|
_ => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn profile_sketch(self) -> Option<SketchId> {
|
||||||
|
match self {
|
||||||
|
Self::Profile(sketch) => Some(sketch),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn icon_of(k: &FeatureKind) -> &'static str {
|
pub(crate) fn icon_of(k: &FeatureKind) -> &'static str {
|
||||||
match k {
|
match k {
|
||||||
FeatureKind::Sketch { .. } => "sketch",
|
FeatureKind::Sketch { .. } => "sketch",
|
||||||
@@ -263,7 +289,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
|||||||
.doc
|
.doc
|
||||||
.features
|
.features
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| (f.id, f.name.clone(), icon_of(&f.kind), f.suppressed, f.kind.type_name(), f.kind.clone()))
|
.map(|f| (f.id, f.name.clone(), icon_of(&f.kind), f.suppressed, f.kind.type_name(), TimelineKind::of(&f.kind)))
|
||||||
.collect();
|
.collect();
|
||||||
// The feature whose body is under the cursor in the viewport.
|
// The feature whose body is under the cursor in the viewport.
|
||||||
let st = app.session.world_state();
|
let st = app.session.world_state();
|
||||||
@@ -373,12 +399,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
|||||||
if resp.double_clicked() {
|
if resp.double_clicked() {
|
||||||
edit = Some(*id);
|
edit = Some(*id);
|
||||||
}
|
}
|
||||||
let sketch_of = match kind {
|
let sketch_of = kind.profile_sketch();
|
||||||
FeatureKind::Extrude { sketch, .. } | FeatureKind::Revolve { sketch, .. } | FeatureKind::Sweep { sketch, .. } => {
|
|
||||||
Some(*sketch)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
resp.context_menu(|ui| {
|
resp.context_menu(|ui| {
|
||||||
if ui.button(crate::i18n::tr_label("Edit Feature")).clicked() {
|
if ui.button(crate::i18n::tr_label("Edit Feature")).clicked() {
|
||||||
edit = Some(*id);
|
edit = Some(*id);
|
||||||
@@ -596,6 +617,27 @@ mod tests {
|
|||||||
h
|
h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_feature_menu_edits_its_profile_sketch_in_both_timelines() {
|
||||||
|
for left in [false, true] {
|
||||||
|
let mut h = Harness::new();
|
||||||
|
h.app.preferences.timeline_left = left;
|
||||||
|
h.app.run("sketch.create", json!({"plane": "XY", "name": "Base"})).unwrap();
|
||||||
|
h.app.run("sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [10, 10]})).unwrap();
|
||||||
|
h.app.run("sketch.finish", json!({})).unwrap();
|
||||||
|
h.app.run("solid.extrude", json!({"sketch": "Base", "distance": 10, "operation": "new", "name": "Depth"})).unwrap();
|
||||||
|
let sketch = h.app.session.doc.find_feature("Base").map(|f| f.id).unwrap();
|
||||||
|
h.frames(4);
|
||||||
|
|
||||||
|
h.step(&json!({"click": {"handle": "timeline:Base"}, "button": "right"})).unwrap();
|
||||||
|
assert!(h.widget("Edit Profile Sketch").is_none(), "left: {left}");
|
||||||
|
h.step(&json!({"key": "Escape"})).unwrap();
|
||||||
|
h.step(&json!({"click": {"handle": "timeline:Depth"}, "button": "right"})).unwrap();
|
||||||
|
h.step(&json!({"widget": "Edit Profile Sketch"})).unwrap();
|
||||||
|
assert_eq!(h.app.session.active_sketch, Some(sketch), "left: {left}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn copied(h: &Harness) -> Vec<String> {
|
fn copied(h: &Harness) -> Vec<String> {
|
||||||
h.app.session.clipboard.iter().map(|f| f.name.clone()).collect()
|
h.app.session.clipboard.iter().map(|f| f.name.clone()).collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
use egui::{Color32, Rect, RichText, Sense, Stroke, pos2, vec2};
|
use egui::{Color32, Rect, RichText, Sense, Stroke, pos2, vec2};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use solidcraft_engine::doc::FeatureKind;
|
|
||||||
|
|
||||||
use crate::theme::Tokens;
|
use crate::theme::Tokens;
|
||||||
use crate::timeline::{clipboard_buttons, delete_feature, delete_features, icon_of, message_button, selected_feature_ids, transport_glyph};
|
use crate::timeline::{
|
||||||
|
TimelineKind, clipboard_buttons, delete_feature, delete_features, icon_of, message_button, selected_feature_ids, transport_glyph,
|
||||||
|
};
|
||||||
use crate::{SolveApp, icons};
|
use crate::{SolveApp, icons};
|
||||||
|
|
||||||
const FOLDER_INDENT: f32 = 14.0;
|
const FOLDER_INDENT: f32 = 14.0;
|
||||||
@@ -427,7 +428,7 @@ fn group_ancestors_are_open(app: &SolveApp, groups: &[solidcraft_engine::doc::Ti
|
|||||||
|
|
||||||
fn group_first_feature(
|
fn group_first_feature(
|
||||||
groups: &[solidcraft_engine::doc::TimelineGroup],
|
groups: &[solidcraft_engine::doc::TimelineGroup],
|
||||||
features: &[(u64, String, &'static str, bool, &'static str, FeatureKind)],
|
features: &[(u64, String, &'static str, bool, &'static str, TimelineKind)],
|
||||||
id: u64,
|
id: u64,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
let group = groups.iter().find(|group| group.id == id)?;
|
let group = groups.iter().find(|group| group.id == id)?;
|
||||||
@@ -438,7 +439,7 @@ fn group_first_feature(
|
|||||||
|
|
||||||
fn group_last_feature(
|
fn group_last_feature(
|
||||||
groups: &[solidcraft_engine::doc::TimelineGroup],
|
groups: &[solidcraft_engine::doc::TimelineGroup],
|
||||||
features: &[(u64, String, &'static str, bool, &'static str, FeatureKind)],
|
features: &[(u64, String, &'static str, bool, &'static str, TimelineKind)],
|
||||||
id: u64,
|
id: u64,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
let group = groups.iter().find(|group| group.id == id)?;
|
let group = groups.iter().find(|group| group.id == id)?;
|
||||||
@@ -449,7 +450,7 @@ fn group_last_feature(
|
|||||||
|
|
||||||
fn visible_order(
|
fn visible_order(
|
||||||
app: &SolveApp,
|
app: &SolveApp,
|
||||||
features: &[(u64, String, &'static str, bool, &'static str, FeatureKind)],
|
features: &[(u64, String, &'static str, bool, &'static str, TimelineKind)],
|
||||||
groups: &[solidcraft_engine::doc::TimelineGroup],
|
groups: &[solidcraft_engine::doc::TimelineGroup],
|
||||||
) -> Vec<TimelineItem> {
|
) -> Vec<TimelineItem> {
|
||||||
let mut order = Vec::new();
|
let mut order = Vec::new();
|
||||||
@@ -615,7 +616,9 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
|||||||
.doc
|
.doc
|
||||||
.features
|
.features
|
||||||
.iter()
|
.iter()
|
||||||
.map(|feature| (feature.id, feature.name.clone(), icon_of(&feature.kind), feature.suppressed, feature.kind.type_name(), feature.kind.clone()))
|
.map(|feature| {
|
||||||
|
(feature.id, feature.name.clone(), icon_of(&feature.kind), feature.suppressed, feature.kind.type_name(), TimelineKind::of(&feature.kind))
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let groups = app.session.doc.timeline_groups.clone();
|
let groups = app.session.doc.timeline_groups.clone();
|
||||||
let order = visible_order(app, &features, &groups);
|
let order = visible_order(app, &features, &groups);
|
||||||
@@ -685,7 +688,7 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
|||||||
}
|
}
|
||||||
slots.push(VerticalSlot { y: ui.cursor().top(), position: index, group: group_id, indent_group });
|
slots.push(VerticalSlot { y: ui.cursor().top(), position: index, group: group_id, indent_group });
|
||||||
let indent = group_id.map(|id| (group_depth(&groups, id) + 1) as f32 * 14.0).unwrap_or(0.0);
|
let indent = group_id.map(|id| (group_depth(&groups, id) + 1) as f32 * 14.0).unwrap_or(0.0);
|
||||||
let has_patterns = matches!(kind, FeatureKind::Sketch { .. }) && crate::sketch_pattern_tree::has_patterns(app, *id);
|
let has_patterns = matches!(kind, TimelineKind::Sketch) && crate::sketch_pattern_tree::has_patterns(app, *id);
|
||||||
let pattern_key = format!("timeline/sketch/{id}/patterns");
|
let pattern_key = format!("timeline/sketch/{id}/patterns");
|
||||||
let patterns_open = crate::browser::is_open(app, &pattern_key, true);
|
let patterns_open = crate::browser::is_open(app, &pattern_key, true);
|
||||||
let (rect, response) = ui.allocate_exact_size(vec2(ui.available_width(), 24.0), Sense::click_and_drag());
|
let (rect, response) = ui.allocate_exact_size(vec2(ui.available_width(), 24.0), Sense::click_and_drag());
|
||||||
@@ -771,10 +774,7 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
|||||||
if response.double_clicked() && !on_fold {
|
if response.double_clicked() && !on_fold {
|
||||||
edit = Some(*id);
|
edit = Some(*id);
|
||||||
}
|
}
|
||||||
let sketch = match kind {
|
let sketch = kind.profile_sketch();
|
||||||
FeatureKind::Extrude { sketch, .. } | FeatureKind::Revolve { sketch, .. } | FeatureKind::Sweep { sketch, .. } => Some(*sketch),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
if response.secondary_clicked() && !selected {
|
if response.secondary_clicked() && !selected {
|
||||||
select_only(app, TimelineItem::Feature(*id));
|
select_only(app, TimelineItem::Feature(*id));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user