use std::collections::HashSet;
use crate::io::{NodeGraphInteractionState, NodeGraphViewState};
use jellyflow_core::core::{Edge, EdgeId, Graph, GroupId, NodeId};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NodeRenderOrderOptions {
pub include_hidden: bool,
pub elevate_nodes_on_select: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EdgeRenderOrderOptions {
pub include_hidden: bool,
pub elevate_edges_on_select: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GroupRenderOrderOptions {
pub elevate_groups_on_select: bool,
}
impl EdgeRenderOrderOptions {
pub fn from_interaction(interaction: &NodeGraphInteractionState) -> Self {
Self {
include_hidden: false,
elevate_edges_on_select: interaction.rendering_interaction().elevate_edges_on_select,
}
}
}
impl Default for EdgeRenderOrderOptions {
fn default() -> Self {
Self {
include_hidden: false,
elevate_edges_on_select: true,
}
}
}
impl GroupRenderOrderOptions {
pub fn from_interaction(interaction: &NodeGraphInteractionState) -> Self {
Self {
elevate_groups_on_select: interaction.rendering_interaction().elevate_nodes_on_select,
}
}
}
impl Default for GroupRenderOrderOptions {
fn default() -> Self {
Self {
elevate_groups_on_select: true,
}
}
}
impl NodeRenderOrderOptions {
pub fn from_interaction(interaction: &NodeGraphInteractionState) -> Self {
Self {
include_hidden: false,
elevate_nodes_on_select: interaction.rendering_interaction().elevate_nodes_on_select,
}
}
}
impl Default for NodeRenderOrderOptions {
fn default() -> Self {
Self {
include_hidden: false,
elevate_nodes_on_select: true,
}
}
}
pub fn resolve_group_render_order(
graph: &Graph,
view_state: &NodeGraphViewState,
options: GroupRenderOrderOptions,
) -> Vec<GroupId> {
let mut seen: HashSet<GroupId> = HashSet::new();
let mut base: Vec<GroupId> = Vec::with_capacity(graph.groups.len());
for id in &view_state.group_draw_order {
if seen.insert(*id) && graph.groups.contains_key(id) {
base.push(*id);
}
}
for id in graph.groups.keys() {
if seen.insert(*id) {
base.push(*id);
}
}
if !options.elevate_groups_on_select || view_state.selected_groups.is_empty() {
return base;
}
let selected: HashSet<GroupId> = view_state.selected_groups.iter().copied().collect();
let mut normal: Vec<GroupId> = Vec::with_capacity(base.len());
let mut elevated: Vec<GroupId> = Vec::new();
for id in base {
if selected.contains(&id) {
elevated.push(id);
} else {
normal.push(id);
}
}
normal.extend(elevated);
normal
}
pub fn resolve_node_render_order(
graph: &Graph,
view_state: &NodeGraphViewState,
options: NodeRenderOrderOptions,
) -> Vec<NodeId> {
let mut seen: HashSet<NodeId> = HashSet::new();
let mut base: Vec<NodeId> = Vec::with_capacity(graph.nodes.len());
for id in &view_state.draw_order {
if seen.insert(*id) && node_is_renderable(graph, *id, options.include_hidden) {
base.push(*id);
}
}
for (id, node) in &graph.nodes {
if seen.insert(*id) && (options.include_hidden || !node.hidden) {
base.push(*id);
}
}
if !options.elevate_nodes_on_select || view_state.selected_nodes.is_empty() {
return base;
}
let selected: HashSet<NodeId> = view_state.selected_nodes.iter().copied().collect();
let mut normal: Vec<NodeId> = Vec::with_capacity(base.len());
let mut elevated: Vec<NodeId> = Vec::new();
for id in base {
if selected.contains(&id) {
elevated.push(id);
} else {
normal.push(id);
}
}
normal.extend(elevated);
normal
}
pub fn resolve_edge_render_order(
graph: &Graph,
view_state: &NodeGraphViewState,
options: EdgeRenderOrderOptions,
) -> Vec<EdgeId> {
let mut seen: HashSet<EdgeId> = HashSet::new();
let mut base: Vec<EdgeId> = Vec::with_capacity(graph.edges.len());
for id in &view_state.edge_draw_order {
let Some(edge) = graph.edges.get(id) else {
continue;
};
if seen.insert(*id) && edge_is_renderable(edge, options.include_hidden) {
base.push(*id);
}
}
for (id, edge) in &graph.edges {
if seen.insert(*id) && edge_is_renderable(edge, options.include_hidden) {
base.push(*id);
}
}
if !options.elevate_edges_on_select
|| (view_state.selected_edges.is_empty() && view_state.selected_nodes.is_empty())
{
return base;
}
let selected_edges: HashSet<EdgeId> = view_state.selected_edges.iter().copied().collect();
let selected_nodes: HashSet<NodeId> = view_state.selected_nodes.iter().copied().collect();
let mut normal: Vec<EdgeId> = Vec::with_capacity(base.len());
let mut elevated: Vec<EdgeId> = Vec::new();
for id in base.drain(..) {
let Some(edge) = graph.edges.get(&id) else {
continue;
};
if edge_is_elevated(graph, id, edge, &selected_edges, &selected_nodes) {
elevated.push(id);
} else {
normal.push(id);
}
}
normal.extend(elevated);
normal
}
fn node_is_renderable(graph: &Graph, id: NodeId, include_hidden: bool) -> bool {
graph
.nodes
.get(&id)
.is_some_and(|node| include_hidden || !node.hidden)
}
fn edge_is_renderable(edge: &Edge, include_hidden: bool) -> bool {
include_hidden || !edge.hidden
}
fn edge_is_elevated(
graph: &Graph,
id: EdgeId,
edge: &Edge,
selected_edges: &HashSet<EdgeId>,
selected_nodes: &HashSet<NodeId>,
) -> bool {
if selected_edges.contains(&id) {
return true;
}
if selected_nodes.is_empty() {
return false;
}
graph
.ports
.get(&edge.from)
.is_some_and(|port| selected_nodes.contains(&port.node))
|| graph
.ports
.get(&edge.to)
.is_some_and(|port| selected_nodes.contains(&port.node))
}