paint bucket back to functionality

This commit is contained in:
Skyler Lehmkuhl 2026-02-28 13:06:41 -05:00
parent 1462df308f
commit 14a2b0a4c2
2 changed files with 25 additions and 6 deletions

View File

@ -55,22 +55,28 @@ impl Action for PaintBucketAction {
// Record for debug test generation (if recording is active) // Record for debug test generation (if recording is active)
dcel.record_paint_point(self.click_point); dcel.record_paint_point(self.click_point);
// Hit-test to find which face was clicked // Find the enclosing cycle for the click point
let face_id = dcel.find_face_containing_point(self.click_point); let query = dcel.find_face_at_point(self.click_point);
// Dump cumulative test to stderr after every paint click (if recording) // Dump cumulative test to stderr after every paint click (if recording)
// Do this before the early return so failed clicks are captured too.
if dcel.is_recording() { if dcel.is_recording() {
eprintln!("\n--- DCEL debug test (cumulative, face={:?}) ---", face_id); eprintln!("\n--- DCEL debug test (cumulative, face={:?}) ---", query.face);
dcel.debug_recorder.as_ref().unwrap().dump_test("test_recorded"); dcel.debug_recorder.as_ref().unwrap().dump_test("test_recorded");
eprintln!("--- end test ---\n"); eprintln!("--- end test ---\n");
} }
if face_id.0 == 0 { if query.cycle_he.is_none() {
// FaceId(0) is the unbounded exterior face — nothing to fill // No edges at all — nothing to fill
return Err("No face at click point".to_string()); return Err("No face at click point".to_string());
} }
// If the cycle is in F0 (no face created yet), create one now
let face_id = if query.face.0 == 0 {
dcel.create_face_at_cycle(query.cycle_he)
} else {
query.face
};
// Store for undo // Store for undo
self.hit_face = Some(face_id); self.hit_face = Some(face_id);
self.old_fill_color = Some(dcel.face(face_id).fill_color.clone()); self.old_fill_color = Some(dcel.face(face_id).fill_color.clone());

View File

@ -528,6 +528,19 @@ impl Dcel {
HalfEdgeId::NONE HalfEdgeId::NONE
} }
/// Create a face from an existing cycle of half-edges in F0.
///
/// Use this when a closed boundary exists but no face was created
/// (e.g. paint bucket on a region that hasn't been filled yet).
/// The cycle's half-edges are assigned to the new face.
/// Returns the new FaceId.
pub fn create_face_at_cycle(&mut self, cycle_he: HalfEdgeId) -> FaceId {
let face = self.alloc_face();
self.faces[face.idx()].outer_half_edge = cycle_he;
self.assign_cycle_face(cycle_he, face);
face
}
/// Re-sort all outgoing half-edges at a vertex by angle and fix the /// Re-sort all outgoing half-edges at a vertex by angle and fix the
/// fan linkage (`twin.next` / `prev`). Call this after operations that /// fan linkage (`twin.next` / `prev`). Call this after operations that
/// add outgoing half-edges to an existing vertex without maintaining /// add outgoing half-edges to an existing vertex without maintaining