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 serde_json::json;
|
||||
use solidcraft_engine::doc::{
|
||||
FeatureKind, PlaneRef,
|
||||
FeatureKind, PlaneRef, SketchId,
|
||||
construct::{AxisDef, PointDef},
|
||||
};
|
||||
|
||||
use crate::theme::Tokens;
|
||||
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 {
|
||||
match k {
|
||||
FeatureKind::Sketch { .. } => "sketch",
|
||||
@@ -263,7 +289,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
.doc
|
||||
.features
|
||||
.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();
|
||||
// The feature whose body is under the cursor in the viewport.
|
||||
let st = app.session.world_state();
|
||||
@@ -373,12 +399,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
if resp.double_clicked() {
|
||||
edit = Some(*id);
|
||||
}
|
||||
let sketch_of = match kind {
|
||||
FeatureKind::Extrude { sketch, .. } | FeatureKind::Revolve { sketch, .. } | FeatureKind::Sweep { sketch, .. } => {
|
||||
Some(*sketch)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let sketch_of = kind.profile_sketch();
|
||||
resp.context_menu(|ui| {
|
||||
if ui.button(crate::i18n::tr_label("Edit Feature")).clicked() {
|
||||
edit = Some(*id);
|
||||
@@ -596,6 +617,27 @@ mod tests {
|
||||
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> {
|
||||
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 serde_json::json;
|
||||
use solidcraft_engine::doc::FeatureKind;
|
||||
|
||||
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};
|
||||
|
||||
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(
|
||||
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,
|
||||
) -> Option<usize> {
|
||||
let group = groups.iter().find(|group| group.id == id)?;
|
||||
@@ -438,7 +439,7 @@ fn group_first_feature(
|
||||
|
||||
fn group_last_feature(
|
||||
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,
|
||||
) -> Option<usize> {
|
||||
let group = groups.iter().find(|group| group.id == id)?;
|
||||
@@ -449,7 +450,7 @@ fn group_last_feature(
|
||||
|
||||
fn visible_order(
|
||||
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],
|
||||
) -> Vec<TimelineItem> {
|
||||
let mut order = Vec::new();
|
||||
@@ -615,7 +616,9 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
.doc
|
||||
.features
|
||||
.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();
|
||||
let groups = app.session.doc.timeline_groups.clone();
|
||||
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 });
|
||||
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 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());
|
||||
@@ -771,10 +774,7 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
if response.double_clicked() && !on_fold {
|
||||
edit = Some(*id);
|
||||
}
|
||||
let sketch = match kind {
|
||||
FeatureKind::Extrude { sketch, .. } | FeatureKind::Revolve { sketch, .. } | FeatureKind::Sweep { sketch, .. } => Some(*sketch),
|
||||
_ => None,
|
||||
};
|
||||
let sketch = kind.profile_sketch();
|
||||
if response.secondary_clicked() && !selected {
|
||||
select_only(app, TimelineItem::Feature(*id));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user