抽离 DFS 批次逻辑至独立模块,并优化节点面积计算与终端过滤流程
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 8m16s
Project CI / Native shell tests (pull_request) Failing after 11m7s

This commit is contained in:
2026-09-11 15:34:30 +08:00
parent b9b4ebd38e
commit 9acc23b848
5 changed files with 71 additions and 66 deletions
@@ -10,6 +10,7 @@ pub use model::*;
pub use persistence::*;
pub use tree::*;
pub use workflow::apply_batch_patch;
pub use workflow::batch::next_image_batch;
pub(crate) use workflow::separate_ui_impl;
#[cfg(test)]
mod tests {
@@ -2,7 +2,6 @@ use super::model::*;
use crate::ui_editor::component::{image::ImageComponent, Component};
use crate::ui_editor::layout::node::Node;
use crate::ui_editor::state::State;
use crate::ui_editor::utils::NodeId;
use std::collections::HashSet;
fn is_unbound_image(node: &Node) -> bool {
@@ -144,68 +143,6 @@ pub fn construct_separation_state(state: &State) -> SeparationState {
}
}
fn terminal_ids(state: &SeparationState) -> HashSet<NodeId> {
state
.bound
.iter()
.map(|n| n.node_id.clone())
.chain(state.problematic_nodes.iter().map(|n| n.node_id.clone()))
.collect()
}
fn collect_dfs_batch<'a>(
node: &'a SeparationNode,
is_root: bool,
root_extractable: bool,
terminal: &HashSet<NodeId>,
selected: &mut Vec<&'a SeparationNode>,
area: &mut u64,
) -> bool {
let is_target = matches!(node.kind, SeparationNodeKind::ImageTarget)
&& (!is_root || root_extractable)
&& !terminal.contains(&node.id);
if is_target {
let node_area = u64::from(node.width_px).saturating_mul(u64::from(node.height_px));
let would_exceed = area.saturating_add(node_area) > IMAGE_EDIT_AREA_LIMIT_PX;
if selected.is_empty() || !would_exceed {
selected.push(node);
*area = area.saturating_add(node_area);
} else {
return true;
}
}
for child in &node.children {
if collect_dfs_batch(child, false, root_extractable, terminal, selected, area) {
return true;
}
}
false
}
pub fn next_image_batch<'a>(
state: &SeparationState,
tree: &'a SeparationTree,
) -> Vec<&'a SeparationNode> {
let terminal = terminal_ids(state);
let mut selected = Vec::new();
let mut area = 0;
collect_dfs_batch(
&tree.root,
true,
tree.root_extractable,
&terminal,
&mut selected,
&mut area,
);
app_log!(
"ui_separation.batch_selected image_id={} image_nodes={} area_px={}",
tree.src_ui_design.as_str(),
selected.len(),
area
);
selected
}
pub fn validate_binding_response(
response: &BindingResp,
batch: &[&SeparationNode],
@@ -0,0 +1,65 @@
use crate::ui_editor::commands::separation::model::*;
use crate::ui_editor::utils::NodeId;
use std::collections::HashSet;
fn terminal_ids(state: &SeparationState) -> HashSet<NodeId> {
state
.bound
.iter()
.map(|n| n.node_id.clone())
.chain(state.problematic_nodes.iter().map(|n| n.node_id.clone()))
.collect()
}
fn collect_dfs_batch<'a>(
node: &'a SeparationNode,
is_root: bool,
root_extractable: bool,
terminal: &HashSet<NodeId>,
selected: &mut Vec<&'a SeparationNode>,
area: &mut u64,
) -> bool {
let is_target = matches!(node.kind, SeparationNodeKind::ImageTarget)
&& (!is_root || root_extractable)
&& !terminal.contains(&node.id);
if is_target {
let node_area = u64::from(node.width_px).saturating_mul(u64::from(node.height_px));
let would_exceed = area.saturating_add(node_area) > IMAGE_EDIT_AREA_LIMIT_PX;
if selected.is_empty() || !would_exceed {
selected.push(node);
*area = area.saturating_add(node_area);
} else {
return true;
}
}
for child in &node.children {
if collect_dfs_batch(child, false, root_extractable, terminal, selected, area) {
return true;
}
}
false
}
pub fn next_image_batch<'a>(
state: &SeparationState,
tree: &'a SeparationTree,
) -> Vec<&'a SeparationNode> {
let terminal = terminal_ids(state);
let mut selected = Vec::new();
let mut area = 0;
collect_dfs_batch(
&tree.root,
true,
tree.root_extractable,
&terminal,
&mut selected,
&mut area,
);
app_log!(
"ui_separation.batch_selected image_id={} image_nodes={} area_px={}",
tree.src_ui_design.as_str(),
selected.len(),
area
);
selected
}
@@ -1,3 +1,4 @@
pub mod batch;
mod binding;
mod cut;
mod extract;
@@ -5,13 +6,13 @@ mod patch;
pub use patch::apply_batch_patch;
use self::batch::next_image_batch;
use super::model::*;
use super::persistence::{
project_relative_path, read_separation_state, separation_dto, separation_sidecar_dir,
separation_state_path, write_separation_state,
};
use super::prompt::gen_extract_prompt;
use super::tree::next_image_batch;
use crate::platform_session::current_platform_session;
use crate::ui_editor::commands::utils::read_ui_reference_image_data_url;
use crate::ui_editor::state::State;
@@ -1,6 +1,7 @@
use super::batch::next_image_batch;
use crate::ui_editor::commands::separation::{
next_image_batch, validate_binding_response, BindingDecision, BindingResp, BoundNode,
ProblematicNode, SeparationNode, SeparationState, MAX_REWORK_COUNT,
validate_binding_response, BindingDecision, BindingResp, BoundNode, ProblematicNode,
SeparationNode, SeparationState, MAX_REWORK_COUNT,
};
use crate::ui_editor::utils::NodeId;
use std::collections::HashMap;