1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::collections::HashSet;

use castep_periodic_table::{
    data::ELEMENT_TABLE,
    element::{Element, LookupElement},
};
use chemrust_core::data::{atom::AtomCollections, Atom};

use crate::{
    analyzer::algorithm::{ideal_bondlength, is_bonded},
    IntersectChecker,
};

use super::algorithm::{FinalReport, Ready};

pub const LOWER_FAC: f64 = 0.6;
pub const UPPER_FAC: f64 = 1.15;

#[derive(Debug, Clone)]
pub struct MountingChecker {
    mount_element: Element,
    mount_distance: f64,
}

impl MountingChecker {
    pub fn new_builder() -> MountingCheckerBuilder {
        MountingCheckerBuilder::new()
    }
    fn available_atoms<'a>(&self, atoms: &'a [Atom]) -> Vec<Atom> {
        atoms
            .iter()
            .filter(|atom| {
                let ideal_bondlength =
                    ideal_bondlength(atom.atomic_number(), self.mount_element.atomic_number());
                is_bonded(self.mount_distance, ideal_bondlength, LOWER_FAC, UPPER_FAC)
            })
            .map(|atom| atom.clone())
            .collect()
    }
    pub fn available_elements(&self, atoms: &[Atom]) -> HashSet<String> {
        atoms
            .iter()
            .filter(|atom| {
                let ideal_bondlength =
                    ideal_bondlength(atom.atomic_number(), self.mount_element.atomic_number());
                is_bonded(self.mount_distance, ideal_bondlength, LOWER_FAC, UPPER_FAC)
            })
            .map(|atom| atom.symbol().into())
            .collect::<Vec<String>>()
            .drain(..)
            .collect::<HashSet<String>>()
    }
    pub fn mount_search<'a>(&self, atoms: &'a [Atom]) -> FinalReport {
        let available_atoms: Vec<Atom> = self.available_atoms(atoms);
        let collections: AtomCollections = available_atoms.into();
        let coords = collections.cartesian_coords().to_vec();
        IntersectChecker::<Ready>::new(&coords)
            .start_with_radius(self.mount_distance)
            .check_spheres()
            .analyze_circle_intersects()
            .analyze_points()
            .report()
            .clone()
    }
}

#[derive(Debug, Clone)]
pub struct MountingCheckerBuilder {
    mount_element: Option<Element>,
    mount_distance: f64,
}

impl MountingCheckerBuilder {
    pub fn new() -> Self {
        Self {
            mount_element: None,
            mount_distance: 0.0,
        }
    }
    pub fn with_element(self, element: &Element) -> Self {
        Self {
            mount_element: Some(element.clone()),
            ..self
        }
    }
    pub fn with_bondlength(self, bond_length: f64) -> Self {
        Self {
            mount_distance: bond_length,
            ..self
        }
    }
    pub fn build(self) -> MountingChecker {
        let mount_element = self
            .mount_element
            .unwrap_or(ELEMENT_TABLE.get_by_symbol("H").unwrap().clone());
        MountingChecker {
            mount_element,
            mount_distance: self.mount_distance,
        }
    }
}