feat(topola-egui): AppearancePanel active_layer selection should be explicit

This commit is contained in:
Ellen Emilia Anna Zscheile 2025-05-30 10:40:38 +02:00
parent d1bcf22e96
commit 3ae298e4e9
4 changed files with 70 additions and 34 deletions

View File

@ -2,6 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use egui::{widget_text::WidgetText, Context, Grid, ScrollArea, SidePanel};
use topola::board::{AccessMesadata, Board}; use topola::board::{AccessMesadata, Board};
pub struct AppearancePanel { pub struct AppearancePanel {
@ -10,40 +11,63 @@ pub struct AppearancePanel {
// In2.Cu shall be #ce7d2c (#e8c39e when selected). // In2.Cu shall be #ce7d2c (#e8c39e when selected).
pub visible: Box<[bool]>, pub visible: Box<[bool]>,
pub active_layer: usize, pub active_layer: Option<usize>,
} }
impl AppearancePanel { impl AppearancePanel {
pub fn new(board: &Board<impl AccessMesadata>) -> Self { pub fn new(board: &Board<impl AccessMesadata>) -> Self {
let layer_count = board.layout().drawing().layer_count(); let layer_count = board.layout().drawing().layer_count();
let visible = std::iter::repeat(true) let visible = core::iter::repeat(true)
.take(layer_count) .take(layer_count)
.collect::<Vec<_>>() .collect::<Box<[_]>>();
.into_boxed_slice();
Self { Self {
visible, visible,
active_layer: 0, active_layer: Some(0),
} }
} }
pub fn update(&mut self, ctx: &egui::Context, board: &Board<impl AccessMesadata>) { pub fn update(&mut self, ctx: &Context, board: &Board<impl AccessMesadata>) {
egui::SidePanel::right("right_side_panel").show(ctx, |ui| { SidePanel::right("appearance_panel").show(ctx, |ui| {
ui.label("Layers"); ui.label("Layers");
let row_height = ui.spacing().interact_size.y;
ScrollArea::vertical().show_rows(
ui,
row_height,
self.visible.len(),
|ui, row_range| {
let start_row = row_range.start;
Grid::new("appearance_layers")
.min_col_width(ui.spacing().icon_width)
.num_columns(3)
.start_row(start_row)
.show(ui, |ui| {
for (layer, visible) in self.visible[row_range].iter_mut().enumerate() {
let layer = layer + start_row;
let layername =
board.layout().drawing().rules().layer_layername(layer);
for (layer, visible) in self.visible.iter_mut().enumerate() { // unnamed layers can't be used for routing
let layername = board if layername.is_some() {
.layout() ui.radio_value(
.drawing() &mut self.active_layer,
.rules() Some(layer),
.layer_layername(layer) WidgetText::default(),
.unwrap_or("Unnamed layer"); );
} else {
let old = *visible; // dummy item to bump the grid
ui.checkbox(visible, layername); ui.label("");
if old != *visible { }
self.active_layer = layer; ui.checkbox(visible, WidgetText::default());
} ui.label(
} layername
.map(|i| i.to_string())
.unwrap_or_else(|| format!("{} - Unnamed layer", layer)),
);
ui.end_row();
}
})
},
);
}); });
} }
} }

View File

@ -8,6 +8,7 @@ use topola::{
autorouter::{ autorouter::{
execution::Command, invoker::InvokerError, selection::Selection, AutorouterOptions, execution::Command, invoker::InvokerError, selection::Selection, AutorouterOptions,
}, },
board::AccessMesadata,
router::RouterOptions, router::RouterOptions,
specctra::{design::SpecctraDesign, ParseError, ParseErrorContext as SpecctraLoadingError}, specctra::{design::SpecctraDesign, ParseError, ParseErrorContext as SpecctraLoadingError},
}; };
@ -281,7 +282,22 @@ impl MenuBar {
) { ) {
} else if workspace_activities_enabled { } else if workspace_activities_enabled {
let mut schedule = |op: fn(Selection, AutorouterOptions) -> Command| { let mut schedule = |op: fn(Selection, AutorouterOptions) -> Command| {
let selection = workspace.overlay.take_selection(); let mut selection = workspace.overlay.take_selection();
if let Some(active_layer) = workspace.appearance_panel.active_layer {
let active_layer = workspace
.interactor
.invoker()
.autorouter()
.board()
.layout()
.rules()
.layer_layername(active_layer)
.expect("unknown active layer");
selection
.pin_selection
.0
.retain(|i| i.layer == active_layer);
}
workspace workspace
.interactor .interactor
.schedule(op(selection, self.autorouter_options)); .schedule(op(selection, self.autorouter_options));

View File

@ -39,7 +39,7 @@ pub enum SelectionMode {
pub struct Overlay { pub struct Overlay {
ratsnest: Ratsnest, ratsnest: Ratsnest,
selection: Selection, pub selection: Selection,
planar_incr_navmesh: Option<PieNavmesh>, planar_incr_navmesh: Option<PieNavmesh>,
reselect_bbox: Option<(SelectionMode, Point)>, reselect_bbox: Option<(SelectionMode, Point)>,
} }
@ -86,6 +86,10 @@ impl Overlay {
use spade::Triangulation; use spade::Triangulation;
use topola::router::planar_incr_embed::navmesh::TrianVertex; use topola::router::planar_incr_embed::navmesh::TrianVertex;
let Some(active_layer) = appearance_panel.active_layer else {
return;
};
if let Ok(triangulation) = if let Ok(triangulation) =
spade::DelaunayTriangulation::<TrianVertex<NodeIndex, f64>>::bulk_load( spade::DelaunayTriangulation::<TrianVertex<NodeIndex, f64>>::bulk_load(
board board
@ -93,16 +97,8 @@ impl Overlay {
.drawing() .drawing()
.rtree() .rtree()
.locate_in_envelope_intersecting(&AABB::<[f64; 3]>::from_corners( .locate_in_envelope_intersecting(&AABB::<[f64; 3]>::from_corners(
[ [-f64::INFINITY, -f64::INFINITY, active_layer as f64],
-f64::INFINITY, [f64::INFINITY, f64::INFINITY, active_layer as f64],
-f64::INFINITY,
appearance_panel.active_layer as f64,
],
[
f64::INFINITY,
f64::INFINITY,
appearance_panel.active_layer as f64,
],
)) ))
.map(|&geom| geom.data) .map(|&geom| geom.data)
.filter_map(|node| { .filter_map(|node| {

View File

@ -74,7 +74,7 @@ impl PinSelector {
} }
#[derive(Debug, Default, Clone, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct PinSelection(BTreeSet<PinSelector>); pub struct PinSelection(pub BTreeSet<PinSelector>);
impl PinSelection { impl PinSelection {
pub fn new() -> Self { pub fn new() -> Self {