抽离 DFS 批次逻辑至独立模块,并优化节点面积计算与终端过滤流程
This commit is contained in:
@@ -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],
|
||||
|
||||
+65
@@ -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
|
||||
}
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+3
-2
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user