use std::collections::{BTreeMap, BTreeSet, VecDeque};
use ifc_model::EntityId;
use crate::connectivity::ConnectionGraph;
use crate::flow::FlowDirection;
use crate::port::Port;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Downstream,
Upstream,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FlowQuery {
pub elements: Vec<EntityId>,
pub used_undirected: bool,
}
pub struct FlowNetwork {
owner: BTreeMap<EntityId, EntityId>,
owned: BTreeMap<EntityId, Vec<EntityId>>,
direction: BTreeMap<EntityId, FlowDirection>,
adjacency: BTreeMap<EntityId, BTreeSet<EntityId>>,
}
impl FlowNetwork {
pub fn build(graph: &ConnectionGraph, ports: &[Port]) -> Self {
let mut owner = BTreeMap::new();
let mut owned: BTreeMap<EntityId, Vec<EntityId>> = BTreeMap::new();
let mut direction = BTreeMap::new();
for port in ports {
direction.insert(port.id, port.flow);
if let Some(element) = port.element {
owner.insert(port.id, element);
owned.entry(element).or_default().push(port.id);
}
}
let mut adjacency: BTreeMap<EntityId, BTreeSet<EntityId>> = BTreeMap::new();
for connection in graph.connections() {
adjacency
.entry(connection.relating)
.or_default()
.insert(connection.related);
adjacency
.entry(connection.related)
.or_default()
.insert(connection.relating);
}
Self {
owner,
owned,
direction,
adjacency,
}
}
fn stated(&self, port: EntityId) -> FlowDirection {
self.direction
.get(&port)
.copied()
.unwrap_or(FlowDirection::NotDefined)
}
fn may_exit(&self, port: EntityId, direction: Direction) -> (bool, bool) {
match (self.stated(port), direction) {
(FlowDirection::Source, Direction::Downstream) => (true, true),
(FlowDirection::Sink, Direction::Downstream) => (false, true),
(FlowDirection::Sink, Direction::Upstream) => (true, true),
(FlowDirection::Source, Direction::Upstream) => (false, true),
(FlowDirection::SourceAndSink, _) => (true, true),
(FlowDirection::NotDefined, _) => (true, false),
}
}
fn may_enter(&self, port: EntityId, direction: Direction) -> (bool, bool) {
match (self.stated(port), direction) {
(FlowDirection::Sink, Direction::Downstream) => (true, true),
(FlowDirection::Source, Direction::Downstream) => (false, true),
(FlowDirection::Source, Direction::Upstream) => (true, true),
(FlowDirection::Sink, Direction::Upstream) => (false, true),
(FlowDirection::SourceAndSink, _) => (true, true),
(FlowDirection::NotDefined, _) => (true, false),
}
}
pub fn query(&self, element: EntityId, direction: Direction) -> FlowQuery {
let mut seen_elements = BTreeSet::new();
let mut queue = VecDeque::new();
let mut used_undirected = false;
seen_elements.insert(element);
queue.push_back(element);
while let Some(current) = queue.pop_front() {
let Some(ports) = self.owned.get(¤t) else {
continue;
};
for &exit in ports {
let (can_exit, exit_stated) = self.may_exit(exit, direction);
if !can_exit {
continue;
}
for next_port in self.adjacency.get(&exit).into_iter().flatten() {
let (can_enter, enter_stated) = self.may_enter(*next_port, direction);
if !can_enter {
continue;
}
let Some(&next) = self.owner.get(next_port) else {
continue;
};
if next == current {
continue;
}
if seen_elements.insert(next) {
if !exit_stated || !enter_stated {
used_undirected = true;
}
queue.push_back(next);
}
}
}
}
seen_elements.remove(&element);
FlowQuery {
elements: seen_elements.into_iter().collect(),
used_undirected,
}
}
pub fn downstream_of(&self, element: EntityId) -> FlowQuery {
self.query(element, Direction::Downstream)
}
pub fn upstream_of(&self, element: EntityId) -> FlowQuery {
self.query(element, Direction::Upstream)
}
}