Skip to content

Commit

Permalink
Start implementing fixed constraint through GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom committed Nov 8, 2023
1 parent a554c75 commit 903e2fc
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 53 deletions.
60 changes: 60 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions detailer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rust-version = "1.71"

[dependencies]
egui = "0.23.0"
egui_extras = "0.23.0"
slotmap = {version = "^1.0", features = ["serde"]}
serde = { version = "1", features = ["derive"] }
drawing = {version = "0.1.0", path = "../drawing"}
126 changes: 101 additions & 25 deletions detailer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use drawing::{handler::ToolResponse, tools, Data, Feature, FeatureKey, Handler};
use drawing::{Constraint, ConstraintKey};

#[derive(Debug, Default, Clone, PartialEq)]
pub enum Tab {
Expand Down Expand Up @@ -59,7 +60,7 @@ impl<'a> Widget<'a> {
};

ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
ui.add_space(4.);
ui.add_space(2.);
egui::warn_if_debug_build(ui);
});
});
Expand All @@ -73,48 +74,126 @@ impl<'a> Widget<'a> {
}

fn show_selection_tab(&mut self, ui: &mut egui::Ui) {
let mut commands: Vec<ToolResponse> = Vec::with_capacity(12);
let mut commands: Vec<ToolResponse> = Vec::with_capacity(4);
let selected: Vec<FeatureKey> = self.drawing.selected_map.keys().map(|k| *k).collect();
for k in selected {
if let Some(v) = self.drawing.features.get_mut(k) {
match v {
Feature::Point(_, x, y) => {
ui.push_id(k, |ui| {
match self.drawing.feature_mut(k) {
Some(Feature::Point(_, x, y)) => {
Widget::show_selection_entry_point(ui, &mut commands, &k, x, y)
}
Feature::LineSegment(_, p1, p2) => {
Some(Feature::LineSegment(_, _p1, _p2)) => {
Widget::show_selection_entry_line(ui, &mut commands, &k)
}
None => {}
}
}

let constraints = self.drawing.constraints_by_feature(&k);
if constraints.len() > 0 {
egui::CollapsingHeader::new("Constraints")
.default_open(true)
.show(ui, |ui| {
for ck in constraints {
match self.drawing.constraint_mut(ck) {
Some(Constraint::Fixed(_, _, x, y)) => {
Widget::show_constraint_fixed(ui, &mut commands, &ck, x, y)
}
None => {}
}
}
});
}
});
}

for c in commands.drain(..) {
self.handler.handle(self.drawing, self.tools, c);
}
}

fn show_constraint_fixed(
ui: &mut egui::Ui,
commands: &mut Vec<ToolResponse>,
k: &ConstraintKey,
px: &mut f32,
py: &mut f32,
) {
ui.horizontal(|ui| {
let r = ui.available_size();
let text_height = egui::TextStyle::Body.resolve(ui.style()).size;

let text_rect = ui
.add_sized(
[r.x / 2., text_height],
egui::Label::new("Fixed").wrap(false),
)
.rect;
ui.add_space(r.x / 2. - text_rect.width() - ui.spacing().item_spacing.x);

ui.add_sized([50., text_height * 1.4], egui::DragValue::new(px));
ui.add_sized([50., text_height * 1.4], egui::DragValue::new(py));
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.button("⊗").clicked() {
commands.push(ToolResponse::ConstraintDelete(*k));
}
});
});
}

fn show_selection_entry_point(
ui: &mut egui::Ui,
commands: &mut Vec<ToolResponse>,
k: &FeatureKey,
px: &mut f32,
py: &mut f32,
) {
// use egui_extras::{Size, StripBuilder};

// StripBuilder::new(ui)
// .size(Size::relative(0.42)) // name cell
// .size(Size::relative(0.23)) // x cell
// .size(Size::relative(0.23)) // y cell
// .size(Size::remainder().at_least(25.0))
// .horizontal(|mut strip| {
// use slotmap::Key;
// strip.cell(|ui| {
// ui.label(format!("Point {:?}", k.data().as_ffi()));
// });
// strip.cell(|ui| {
// ui.add(egui::DragValue::new(px));
// });
// strip.cell(|ui| {
// ui.add(egui::DragValue::new(py));
// });
// strip.cell(|ui| {
// if ui.button("⊗").clicked() {
// commands.push(ToolResponse::Delete(*k));
// }
// });
// });

ui.horizontal(|ui| {
let r = ui.available_size();
let text_height = egui::TextStyle::Body.resolve(ui.style()).size;

use slotmap::Key;
ui.add_sized(
[r.x / 2., text_height],
egui::Label::new(format!("Point {:?}", k.data().as_ffi())).wrap(false),
);

ui.add_sized([r.x / 6., text_height * 1.4], egui::DragValue::new(px));
ui.add_sized([r.x / 6., text_height * 1.4], egui::DragValue::new(py));
if ui.button("⊗").clicked() {
commands.push(ToolResponse::Delete(*k));
let text_rect = ui
.add_sized(
[r.x / 2., text_height],
egui::Label::new(format!("Point {:?}", k.data())).wrap(false),
)
.rect;
if text_rect.width() < r.x / 2. {
ui.add_space(r.x / 2. - text_rect.width());
}

ui.add_sized([50., text_height * 1.4], egui::DragValue::new(px));
ui.add_sized([50., text_height * 1.4], egui::DragValue::new(py));
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.button("⊗").clicked() {
commands.push(ToolResponse::Delete(*k));
}
});
});
}

Expand All @@ -130,17 +209,14 @@ impl<'a> Widget<'a> {
use slotmap::Key;
ui.add_sized(
[r.x / 2., text_height],
egui::Label::new(format!("Line {:?}", k.data().as_ffi())).wrap(false),
);

ui.allocate_exact_size(
[r.x / 3. + ui.spacing().item_spacing.x, text_height * 1.4].into(),
egui::Sense::click(),
egui::Label::new(format!("Line {:?}", k.data())).wrap(false),
);

if ui.button("⊗").clicked() {
commands.push(ToolResponse::Delete(*k));
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.button("⊗").clicked() {
commands.push(ToolResponse::Delete(*k));
}
});
});
}

Expand Down
8 changes: 8 additions & 0 deletions drawing/src/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@ impl Constraint {
Fixed(..) => matches!(ft, &Feature::Point(..)),
}
}

pub fn conflicts(&self, other: &Constraint) -> bool {
use Constraint::Fixed;
match (self, other) {
(Fixed(_, f1, _, _), Fixed(_, f2, _, _)) => f1 == f2,
_ => false,
}
}
}
72 changes: 71 additions & 1 deletion drawing/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl ConstraintData {
let mut by_feature = HashMap::with_capacity(2 * self.constraints.len());
for (ck, c) in self.constraints.iter() {
for fk in c.affecting_features() {
if by_feature.contains_key(&fk) {
if !by_feature.contains_key(&fk) {
by_feature.insert(fk, HashSet::from([ck]));
} else {
by_feature.get_mut(&fk).unwrap().insert(ck);
Expand All @@ -66,6 +66,54 @@ impl ConstraintData {

self.by_feature = by_feature;
}

pub fn add(&mut self, c: Constraint) {
for c2 in self.constraints.values() {
if c.conflicts(c2) {
return;
}
}

let k = self.constraints.insert(c.clone());
for fk in c.affecting_features() {
if !self.by_feature.contains_key(&fk) {
self.by_feature.insert(fk, HashSet::from([k]));
} else {
self.by_feature.get_mut(&fk).unwrap().insert(k);
}
}
}

pub fn delete(&mut self, ck: ConstraintKey) {
match self.constraints.remove(ck) {
Some(c) => {
for fk in c.affecting_features() {
let remaining_entries = if let Some(set) = self.by_feature.get_mut(&fk) {
set.remove(&ck);
set.len()
} else {
99999
};

if remaining_entries == 0 {
self.by_feature.remove(&fk);
}
}
}
None => {}
}
}

pub fn by_feature(&self, k: &FeatureKey) -> Vec<ConstraintKey> {
match self.by_feature.get(k) {
Some(set) => set.iter().map(|ck| ck.clone()).collect(),
None => vec![],
}
}

pub fn get_mut<'a>(&'a mut self, ck: ConstraintKey) -> Option<&'a mut Constraint> {
self.constraints.get_mut(ck)
}
}

/// Data stores state about the drawing and what it is composed of.
Expand All @@ -90,6 +138,28 @@ impl Default for Data {
}

impl Data {
pub fn feature_mut<'a>(&'a mut self, k: FeatureKey) -> Option<&'a mut Feature> {
let Data { features, .. } = self;

features.get_mut(k)
}

pub fn constraint_mut<'a>(&'a mut self, ck: ConstraintKey) -> Option<&'a mut Constraint> {
self.constraints.get_mut(ck)
}

pub fn constraints_by_feature(&self, k: &FeatureKey) -> Vec<ConstraintKey> {
self.constraints.by_feature(k)
}

pub fn add_constraint(&mut self, c: Constraint) {
self.constraints.add(c);
}

pub fn delete_constraint(&mut self, k: ConstraintKey) {
self.constraints.delete(k);
}

pub fn find_point_at(&self, p: egui::Pos2) -> Option<FeatureKey> {
for (k, v) in self.features.iter() {
if v.bb(self).center().distance_sq(p) < 0.0001 {
Expand Down
Loading

0 comments on commit 903e2fc

Please sign in to comment.