use super::{Design, Domain, Helix, Strand};
#[derive(Clone, Debug)]
pub enum Patch {
ElongateDomain(usize, bool, isize, bool, usize),
ShortenDomain(usize, bool, isize, bool, usize),
AddHelix(Helix),
DeleteHelix(usize),
AddStrand(usize, bool, isize),
DeleteStrand(usize, bool, isize),
CrossOver(usize, bool, usize, isize, bool),
MakeBound(usize, bool, isize, usize, bool, isize),
CutBound(usize, bool, isize, usize, bool, isize),
}
impl Patch {
pub fn apply<S: serde::Serialize, T: serde::Serialize>(&self, design: &mut Design<S, T>) {
match self {
Patch::ElongateDomain(h, b1, n1, b2, n2) => {
let s_id = design
.get_strand_nucl(*h as isize, *n1, *b1)
.expect("get nucl elgonate domain");
let strand = &mut design.strands[s_id];
let mut dom_num = 0;
for i in 0..strand.domains.len() {
if strand.domains[i].contains(*h as isize, *n1, *b1) {
dom_num = i;
break;
}
}
let domain = &mut strand.domains[dom_num];
if !domain.contains(*h as isize, *n1, *b1) {
panic!("domain does not contains");
}
if *b2 == domain.forward {
domain.end += *n2 as isize
} else {
domain.start -= *n2 as isize
};
}
Patch::ShortenDomain(h, b1, n1, b2, n2) => {
let s_id = design
.get_strand_nucl(*h as isize, *n1, *b1)
.expect("get nucl shorten domain");
let strand = &mut design.strands[s_id];
let mut dom_num = 0;
for i in 0..strand.domains.len() {
if strand.domains[i].contains(*h as isize, *n1, *b1) {
dom_num = i;
break;
}
}
let domain = &mut strand.domains[dom_num];
if !domain.contains(*h as isize, *n1, *b1) {
panic!("domain does not contains");
}
if *b2 == domain.forward {
domain.end -= *n2 as isize
} else {
domain.start += *n2 as isize
};
}
Patch::AddHelix(h) => {
design.add_helix(h.position.x, h.position.y, h.position.z, h.roll, h.pitch, h.yaw);
}
Patch::DeleteHelix(h) => {
design.helices.remove(*h);
}
Patch::AddStrand(h, b, n) => {
design.strands.push(Strand {
domains: vec![Domain {
helix: *h as isize,
start: *n,
end: *n + 1,
forward: *b,
label: None,
sequence: None,
}],
label: None,
sequence: None,
cyclic: false,
color: None,
});
}
Patch::DeleteStrand(h, b, n) => {
if let Some(s_id) = design.get_strand_nucl(*h as isize, *n, *b) {
design.strands.remove(s_id);
}
}
Patch::CrossOver(s, b1, h, n, b2) => {
let new_dom = Domain {
helix: *h as isize,
start: *n,
end: *n + 1,
forward: *b2,
label: None,
sequence: None,
};
if *b1 {
design.strands[*s].domains.push(new_dom);
} else {
design.strands[*s].domains.insert(0, new_dom);
}
}
Patch::MakeBound(h1, b1, n1, h2, b2, n2) => {
let s1 = design
.get_strand_nucl(*h1 as isize, *n1, *b1)
.expect("bounding unexisting nucl");
let s2 = design
.get_strand_nucl(*h2 as isize, *n2, *b2)
.expect("bounding unexisting nucl");
if s1 != s2 {
for i in 0..design.strands[s2].domains.len() {
let domain = design.strands[s2].domains[i].pseudo_copy();
design.strands[s1].domains.push(domain);
}
design.strands.remove(s2);
} else {
let builder = design.get_strand_ref(s1);
builder.cycle();
}
}
Patch::CutBound(h1, b1, n1, h2, b2, n2) => {
let h1 = *h1 as isize;
let s1 = design
.get_strand_nucl(h1, *n1, *b1)
.expect("cuting on unexisting nucl");
let strand = &mut design.strands[s1];
if !strand.cyclic {
let mut dom = 0;
let mut old_start = 0;
let mut old_end = 0;
let mut split_dom = true;
for i in 0..strand.domains.len() {
let domain = &mut strand.domains[i];
if domain.contains(h1, *n1, *b1) {
old_start = domain.start;
old_end = domain.end;
let contains = domain.contains(*h2 as isize, *n2, *b2);
if domain.forward {
domain.end = *n1 + 1;
} else {
domain.start = *n1;
}
if !contains {
old_start = strand.domains[i + 1].start;
old_end = strand.domains[i + 1].end;
dom = i + 1;
split_dom = false;
} else {
dom = i;
}
break;
}
}
let old_domain = &strand.domains[dom];
let new_domain = Domain {
start: {
if old_domain.forward {
*n2
} else {
old_start
}
},
end: {
if old_domain.forward {
old_end
} else {
*n2 + 1
}
},
label: None,
sequence: None,
..*old_domain
};
let nb_dom = strand.domains.len();
let mut domains = vec![new_domain];
for i in (dom + 1)..nb_dom {
let domain = strand.domains[i].pseudo_copy();
domains.push(domain);
}
if !split_dom {
dom -= 1;
}
for _ in (dom + 1)..nb_dom {
strand.domains.pop();
}
design.strands.push(Strand {
domains,
label: None,
sequence: None,
cyclic: false,
color: None,
});
} else {
let mut domains = Vec::new();
let mut added_dom = None;
for i in 0..strand.domains.len() {
if strand.domains[i].contains(*h2 as isize, *n2, *b2) {
let contains = strand.domains[i].contains(h1, *n1, *b1);
let old_start = strand.domains[i].start;
let old_end = strand.domains[i].end;
if strand.domains[i].forward {
strand.domains[i].start = *n2;
} else {
strand.domains[i].end = *n2 + 1;
}
if i == 0 {
let last_dom = strand.domains.last_mut().unwrap();
if last_dom.forward {
last_dom.end = *n2;
} else {
last_dom.start = *n2 + 1;
}
} else {
if contains {
let dom = strand.domains[i].pseudo_copy();
let start = if dom.forward { old_start } else { *n1 };
let end = if dom.forward { *n1 + 1 } else { old_end };
added_dom = Some(Domain { start, end, ..dom });
}
let last_dom = strand.domains.last_mut().unwrap();
if last_dom.length() == 1 {
strand.domains.pop();
} else {
if last_dom.forward {
last_dom.end -= 1;
} else {
last_dom.start += 1;
}
}
for j in i..strand.domains.len() + i {
let dom =
strand.domains[j % strand.domains.len()].pseudo_copy();
domains.push(dom);
}
if let Some(ref dom) = added_dom {
domains.push(dom.pseudo_copy());
}
}
break;
}
}
if domains.len() > 0 {
strand.domains = domains;
}
strand.cyclic = false;
}
}
}
}
pub fn undo(&self) -> Self {
match self {
Patch::ElongateDomain(h, b1, n1, b2, n2) if *b1 == *b2 => {
Patch::ShortenDomain(*h, *b1, *n1 + *n2 as isize, *b2, *n2)
}
Patch::ElongateDomain(h, b1, n1, b2, n2) => {
Patch::ShortenDomain(*h, *b1, *n1 - *n2 as isize, *b2, *n2)
}
Patch::ShortenDomain(h, b1, n1, b2, n2) if *b1 == *b2 => {
Patch::ElongateDomain(*h, *b1, *n1 - *n2 as isize, *b2, *n2)
}
Patch::ShortenDomain(h, b1, n1, b2, n2) => {
Patch::ElongateDomain(*h, *b1, *n1 + *n2 as isize, *b2, *n2)
}
Patch::AddStrand(h, b, n) => Patch::DeleteStrand(*h, *b, *n),
Patch::DeleteStrand(h, b, n) => Patch::AddStrand(*h, *b, *n),
Patch::MakeBound(h1, b1, n1, h2, b2, n2) => {
Patch::CutBound(*h1, *b1, *n1, *h2, *b2, *n2)
}
Patch::CutBound(h1, b1, n1, h2, b2, n2) => {
Patch::MakeBound(*h1, *b1, *n1, *h2, *b2, *n2)
}
_ => unimplemented!(),
}
}
}