#![deny(missing_docs)]
#[macro_use]
extern crate serde_derive;
extern crate failure;
extern crate serde;
pub use failure::Error;
use std::borrow::Cow;
use std::collections::HashMap;
use std::f64::consts::PI;
pub mod geometry;
use crate::geometry::*;
pub mod sequences;
pub mod cadnano;
#[derive(Serialize, Deserialize)]
pub struct Design<StrandLabel, DomainLabel> {
pub version: String,
pub helices: Vec<Helix>,
pub strands: Vec<Strand<StrandLabel, DomainLabel>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub parameters: Option<Parameters>,
}
fn is_false(x: &bool) -> bool {
!*x
}
fn none<Label>() -> Option<Label> {
None
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Strand<Label, DomainLabel> {
pub domains: Vec<Domain<DomainLabel>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub sequence: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "is_false", default)]
pub cyclic: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub color: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none", default = "none")]
pub label: Option<Label>,
}
impl<Label, DomainLabel> Strand<Label, DomainLabel> {
pub fn find_nucl(&self, nucl: isize) -> (isize, isize, bool) {
let mut current = 0;
let mut i = 0;
while current + &self.domains[i].length() <= nucl {
current += &self.domains[i].length();
i += 1;
}
let position = if self.domains[i].right {
self.domains[i].start + nucl - current
} else {
self.domains[i].end - 1 - nucl + current
};
(self.domains[i].helix, position, self.domains[i].right)
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Domain<Label> {
pub helix: isize,
pub start: isize,
pub end: isize,
pub right: bool,
#[serde(skip_serializing_if = "Option::is_none", default = "none")]
pub label: Option<Label>,
pub sequence: Option<Cow<'static, str>>,
}
impl<Label> Domain<Label> {
pub fn iter(&self) -> DomainIter {
DomainIter {
start: self.start,
end: self.end,
right: self.right,
}
}
pub fn translate(self, dx: isize, dy: isize) -> Self {
use std::convert::TryFrom;
Domain {
start: self.start + dx,
end: self.end + dx,
helix: usize::try_from(self.helix as isize + dy).unwrap() as isize,
..self
}
}
pub fn shift_x(self, dx: isize) -> Self {
Domain {
start: self.start + dx,
end: self.end + dx,
..self
}
}
pub fn shift_y(self, dy: isize) -> Self {
use std::convert::TryFrom;
Domain {
helix: usize::try_from(self.helix as isize + dy).unwrap() as isize,
..self
}
}
pub fn length(&self) -> isize {
self.end - self.start
}
}
pub struct DomainIter {
start: isize,
end: isize,
right: bool,
}
impl Iterator for DomainIter {
type Item = isize;
fn next(&mut self) -> Option<Self::Item> {
if self.start == self.end {
None
} else {
if self.right {
let s = self.start;
self.start += 1;
Some(s)
} else {
let s = self.end;
self.end -= 1;
Some(s - 1)
}
}
}
}
pub const M13_7249: &'static str = include_str!("m13");
pub const M13_594: &'static str = include_str!("m13_594");
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Parameters {
pub z_step: f64,
pub helix_radius: f64,
pub bases_per_turn: f64,
pub groove_angle: f64,
pub inter_helix_gap: f64,
}
impl Default for Parameters {
fn default() -> Self {
Parameters {
z_step: 0.332,
helix_radius: 1.,
bases_per_turn: 10.44,
groove_angle: -24. * PI / 34.,
inter_helix_gap: 1.,
}
}
}
#[derive(Serialize, Deserialize)]
pub struct Helix {
pub origin: Point<f64>,
pub roll: f64,
pub yaw: f64,
pub pitch: f64,
}
impl Helix {
pub fn theta(&self, n: isize, right: bool, cst: &Parameters) -> f64 {
let shift = if right {
cst.groove_angle
} else {
0.
};
n as f64 * 2. * PI / cst.bases_per_turn + shift + self.roll
}
pub fn space_pos(&self, p: &Parameters, n: isize, right: bool) -> [f64; 3] {
let theta = self.theta(n, right, p);
let mut ret = [
n as f64 * p.z_step,
theta.cos() * p.helix_radius,
theta.sin() * p.helix_radius,
];
let forward = [self.yaw.cos() * self.pitch.cos(),
self.pitch.sin(),
-self.yaw.sin() * self.pitch.cos()];
let right = [self.yaw.sin(),
0.,
self.yaw.cos()];
let up = [ right[1] * forward[2] - right[2] * forward[1],
right[2] * forward[0] - right[0] * forward[2],
right[0] * forward[1] - right[1] * forward[0]];
ret = [ret[0] * forward[0] + ret[1] * up[0] + ret[2] * right[0],
ret[0] * forward[1] + ret[1] * up[1] + ret[2] * right[1],
ret[0] * forward[2] + ret[1] * up[2] + ret[2] * right[2],
];
ret[0] += self.origin.x;
ret[1] += self.origin.y;
ret[2] += self.origin.z;
ret
}
pub fn axis_pos(&self, p: &Parameters, n:isize) -> [f64; 3] {
let mut ret = [
n as f64 * p.z_step,
0.,
0.,
];
let forward = [self.yaw.cos() * self.pitch.cos(),
self.pitch.sin(),
-self.yaw.sin() * self.pitch.cos()];
let right = [self.yaw.sin(),
0.,
self.yaw.cos()];
let up = [ right[1] * forward[2] - right[2] * forward[1],
right[2] * forward[0] - right[0] * forward[2],
right[0] * forward[1] - right[1] * forward[0]];
ret = [ret[0] * forward[0] + ret[1] * up[0] + ret[2] * right[0],
ret[0] * forward[1] + ret[1] * up[1] + ret[2] * right[1],
ret[0] * forward[2] + ret[1] * up[2] + ret[2] * right[2],
];
ret[0] += self.origin.x;
ret[1] += self.origin.y;
ret[2] += self.origin.z;
ret
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HelixId(pub isize);
impl From<isize> for HelixId {
fn from(e: isize) -> Self {
HelixId(e)
}
}
impl From<i32> for HelixId {
fn from(e: i32) -> Self {
HelixId(e as isize)
}
}
impl From<usize> for HelixId {
fn from(e: usize) -> Self {
HelixId(e as isize)
}
}
pub struct StrandId(pub usize);
#[doc(hidden)]
#[derive(Serialize, Deserialize)]
pub struct Res {
pub out: String,
pub err: String,
}
pub struct StrandRef<'a, StrandLabel, DomainLabel> {
builder: &'a mut Design<StrandLabel, DomainLabel>,
strand_id: usize,
x: Option<isize>,
x0: Option<isize>,
}
impl<StrandLabel: serde::Serialize, DomainLabel: serde::Serialize>
Design<StrandLabel, DomainLabel>
{
pub fn new() -> Self {
Design {
version: env!("CARGO_PKG_VERSION").to_string(),
helices: Vec::new(),
strands: Vec::new(),
parameters: Some(Parameters::default()),
}
}
pub fn square_grid_helix(&self, h: isize, v: isize) -> Helix {
Helix {
origin: Point {
x: 0.,
y: h as f64
* (self.parameters.unwrap().helix_radius * 2.
+ self.parameters.unwrap().inter_helix_gap),
z: v as f64
* (self.parameters.unwrap().helix_radius * 2.
+ self.parameters.unwrap().inter_helix_gap),
},
roll: 0.,
pitch: 0.,
yaw: 0.,
}
}
pub fn adjusted_grid_helix(&self, h: isize, v: isize) -> Helix {
let delta_roll = self.parameters.unwrap().groove_angle - PI;
Helix {
origin: Point {
x: 0.,
y: h as f64
* (self.parameters.unwrap().helix_radius * 2.
+ self.parameters.unwrap().inter_helix_gap),
z: v as f64
* (self.parameters.unwrap().helix_radius * 2.
+ self.parameters.unwrap().inter_helix_gap),
},
roll: ((h + v) as f64) * delta_roll,
pitch: 0.,
yaw: 0.,
}
}
pub fn with_parameters(parameters: Parameters) -> Self {
let mut b = Design::new();
b.parameters = Some(parameters);
b
}
pub fn add_grid_helix(&mut self, h: isize, v: isize) -> HelixId {
self.helices.push(self.square_grid_helix(h, v));
HelixId((self.helices.len() - 1) as isize)
}
pub fn add_adjusted_grid_helix(&mut self, h: isize, v: isize) -> HelixId {
self.helices.push(self.adjusted_grid_helix(h, v));
HelixId((self.helices.len() - 1) as isize)
}
pub fn add_helix(
&mut self,
x: f64,
y: f64,
z: f64,
roll: f64,
pitch: f64,
yaw: f64,
) -> HelixId {
self.helices.push(Helix {
origin: Point { x, y, z },
roll,
pitch,
yaw,
});
HelixId((self.helices.len() - 1) as isize)
}
pub fn strand<H: Into<HelixId>>(
&mut self,
helix: H,
start: isize,
) -> StrandRef<StrandLabel, DomainLabel> {
let strand_id = self.strands.len();
self.strands.push(Strand {
domains: vec![Domain {
helix: helix.into().0,
start,
end: start,
right: true,
label: None,
sequence: None,
}],
label: None,
sequence: None,
cyclic: false,
color: None,
});
StrandRef {
strand_id,
builder: self,
x: Some(start),
x0: Some(start),
}
}
pub fn empty_strand(&mut self) -> StrandRef<StrandLabel, DomainLabel> {
let strand_id = self.strands.len();
self.strands.push(Strand {
domains: Vec::new(),
cyclic: false,
color: None,
sequence: None,
label: None,
});
StrandRef {
strand_id,
builder: self,
x: None,
x0: None,
}
}
pub fn get_strand_ref(&mut self, strand_id: usize) -> StrandRef<StrandLabel, DomainLabel> {
let strand = &self.strands[strand_id];
if strand.cyclic {
panic!("Attempt to keep building on a cyclic strand");
}
let domain = &strand.domains[strand.domains.len() - 1];
let x0 = if domain.right {
domain.start
} else {
domain.end - 1
};
let x = if domain.right {
domain.end - 1
} else {
domain.start
};
StrandRef {
strand_id,
builder: self,
x: Some(x),
x0: Some(x0)
}
}
pub fn write(&self) -> Result<(), Error> {
serde_json::to_writer(std::io::stdout(), self)?;
Ok(())
}
pub fn write_to<P: AsRef<std::path::Path>>(&self, p: P) -> Result<(), Error> {
if std::env::var("REMOTE").is_ok() {
serde_json::to_writer_pretty(std::io::stdout(), &self)?
} else {
serde_json::to_writer_pretty(std::fs::File::create(p)?, &self)?
}
Ok(())
}
pub fn unique_positions(&self) -> Option<(isize, isize, bool, usize, usize)> {
let mut positions = HashMap::new();
for (i, s) in self.strands.iter().enumerate() {
for dom in s.domains.iter() {
for pos in dom.iter() {
if let Some(prev) = positions.insert((dom.helix, pos, dom.right), i) {
if s.cyclic {
let j = positions.get(&(s.domains[0].helix, s.domains[0].iter().next().unwrap(),
s.domains[0].right));
if let Some(x) = j {
if *x == prev {
continue;
}
}
}
return Some((dom.helix, pos, dom.right, i, prev));
}
}
}
}
None
}
pub fn check_and_write_to<P: AsRef<std::path::Path>>(&self, p: P) -> Result<(), Error> {
if let Some((helix, pos, right, i, prev)) = self.unique_positions() {
let sens = if right { "Sens" } else { "AntiSens" };
println!("WARNING: SOME STRANDS ARE CONFLICTING");
println!("First conflict found: On helix {}, at position {}, on the {} strand",
helix,
pos,
sens);
println!("Strands number {} and {} are in conflict", i, prev);
}
self.write_to(p)
}
fn get_cross_overs(&self) -> Vec<(isize, isize, bool, isize, isize, bool)> {
let mut ret = Vec::new();
for s in &self.strands {
for i in 0..(s.domains.len() - 1) {
if s.domains[i].helix != s.domains[i + 1].helix {
let origin = if s.domains[i].right {
s.domains[i].end - 1
} else {
s.domains[i].start
};
let destination = if s.domains[i + 1].right {
s.domains[i + 1].start
} else {
s.domains[i + 1].end - 1
};
ret.push((s.domains[i].helix,
origin,
s.domains[i].right,
s.domains[i + 1].helix,
destination,
s.domains[i + 1].right));
}
}
}
ret
}
fn get_cross_overs_helices(&self, h1: isize, h2:isize) -> Vec<(isize, bool, isize, bool)> {
let cross_overs = self.get_cross_overs();
let mut ret = Vec::new();
for (g1, x1, b1, g2, x2, b2) in cross_overs {
if g1 == h1 && g2 == h2 {
ret.push((x1, b1, x2, b2));
} else if g2 == h1 && g1 == h2 {
ret.push((x2, b2, x1, b1));
}
}
ret
}
fn get_total_dist_helices(&self, h1: isize, h2:isize, theta: f64, phi: f64) -> f64 {
let cross_overs = self.get_cross_overs_helices(h1, h2);
let helix1 = Helix {
roll: theta,
..self.helices[h1 as usize]
};
let helix2 = Helix {
roll: phi,
..self.helices[h2 as usize]
};
let mut ret = 0.;
for (x1, b1, x2, b2) in cross_overs {
let pos_1 = helix1.space_pos(&self.parameters.unwrap(), x1, b1);
let pos_2 = helix2.space_pos(&self.parameters.unwrap(), x2, b2);
ret += (pos_1[0] - pos_2[0]) * (pos_1[0] - pos_2[0]);
ret += (pos_1[1] - pos_2[1]) * (pos_1[1] - pos_2[1]);
ret += (pos_1[2] - pos_2[2]) * (pos_1[2] - pos_2[2]);
}
ret
}
pub fn adjust_helix(&mut self, h1: usize, h2: usize) {
let mut theta = self.helices[h1].roll;
let mut phi = self.helices[h2].roll;
loop {
let best = self.get_total_dist_helices(h1 as isize, h2 as isize, theta, phi);
if self.get_total_dist_helices(h1 as isize, h2 as isize, theta - 0.1, phi - 0.1) < best {
theta -= 0.1;
phi -= 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta + 0.1, phi - 0.1) < best {
theta += 0.1;
phi -= 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta - 0.1, phi + 0.1) < best {
theta -= 0.1;
phi += 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta + 0.1, phi + 0.1) < best {
theta += 0.1;
phi += 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta, phi + 0.1) < best {
phi += 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta, phi - 0.1) < best {
phi -= 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta - 0.1, phi) < best {
theta -= 0.1;
} else if self.get_total_dist_helices(h1 as isize, h2 as isize, theta + 0.1, phi) < best {
theta += 0.1;
} else {
self.helices[h1].roll = theta;
self.helices[h2].roll = phi;
return;
}
}
}
}
const KELLY: [u32; 19] = [
0xF3C300, 0x875692,
0xA1CAF1, 0xBE0032, 0xC2B280, 0x848482, 0x008856, 0xE68FAC, 0x0067A5, 0xF99379, 0x604E97,
0xF6A600, 0xB3446C, 0xDCD300, 0x882D17, 0x8DB600, 0x654522, 0xE25822, 0x2B3D26,
];
impl<StrandLabel, DomainLabel> Strand<StrandLabel, DomainLabel> {
pub fn default_color(&self) -> u32 {
for domain in self.domains.iter() {
let x1 = if domain.right {
domain.end - 1
} else {
domain.start
};
let h = domain.helix as isize;
let x = x1 + (x1 % 11) + 5 * h;
let n = KELLY.len() as isize;
return KELLY[(((x % n) + n) % n) as usize];
}
0
}
pub fn set_kelly_color(&mut self, n: usize) {
self.color = Some(KELLY[n % KELLY.len()]);
}
}
impl<'a:'b, 'b, StrandLabel, DomainLabel> StrandRef<'a, StrandLabel, DomainLabel> {
pub fn id(&self) -> StrandId {
StrandId(self.strand_id)
}
pub fn design(&mut self) -> &mut Design<StrandLabel, DomainLabel> {
self.builder
}
pub fn len(&self) -> usize {
self.domains()
.iter()
.map(|d| (d.end - d.start) as usize)
.sum()
}
pub fn with_color(self, color: u32) -> Self {
self.builder.strands[self.strand_id].color = Some(color);
self
}
pub fn with_kelly_color(self, color: usize) -> Self {
self.builder.strands[self.strand_id].color = Some(KELLY[color]);
self
}
pub fn domains(&self) -> &[Domain<DomainLabel>] {
&self.builder.strands[self.strand_id].domains
}
pub fn domains_mut(&mut self) -> &mut Vec<Domain<DomainLabel>> {
&mut self.builder.strands[self.strand_id].domains
}
pub fn push_domain(&mut self, domain: Domain<DomainLabel>) {
self.x = Some(if domain.right {
domain.end - 1
} else {
domain.start
});
self.builder.strands[self.strand_id].domains.push(domain);
}
pub fn extend<I: Iterator<Item = Domain<DomainLabel>>>(&mut self, i: I) {
for i in i {
self.push_domain(i)
}
}
pub fn with_sequence<I: Into<Cow<'static, str>>>(self, sequence: I) -> Self {
self.builder.strands[self.strand_id].sequence = Some(sequence.into());
self
}
pub fn with_domain_sequence<I: Into<Cow<'static, str>>>(self, sequence: I) -> Self {
self.builder.strands[self.strand_id]
.domains
.last_mut()
.unwrap()
.sequence = Some(sequence.into());
self
}
pub fn to(mut self, x: isize) -> Self {
let strand = self.builder.strands[self.strand_id]
.domains
.last_mut()
.unwrap();
self.x = Some(x);
let x0 = self.x0.unwrap();
if x > x0 {
strand.right = true;
strand.start = x0;
strand.end = x + 1;
} else {
strand.right = false;
strand.start = x;
strand.end = x0 + 1;
}
self
}
pub fn pop(mut self) -> Self {
self.builder.strands[self.strand_id].domains.pop();
if let Some(last) = self.builder.strands[self.strand_id].domains.last() {
self.x = Some(if last.right {
last.end - 1
} else {
last.start
})
} else {
self.x = None
}
self
}
pub fn rev_to(mut self, x: isize) -> Self {
if self.builder.strands[self.strand_id].domains.is_empty() {
return self;
}
let ref mut strand = self.builder.strands[self.strand_id].domains[0];
self.x = Some(x);
let x0 = self.x0.unwrap();
if x > x0 {
strand.right = true;
strand.start = x0;
strand.end = x + 1;
} else {
strand.right = false;
strand.start = x;
strand.end = x0 + 1;
}
self
}
pub fn with_label<L: Into<StrandLabel>>(self, label: L) -> Self {
self.builder.strands[self.strand_id].label = Some(label.into());
self
}
pub fn with_domain_label<L: Into<DomainLabel>>(self, label: L) -> Self {
self.builder.strands[self.strand_id]
.domains
.last_mut()
.unwrap()
.label = Some(label.into());
self
}
pub fn next_domain_to(mut self, to: isize) -> Self {
let helix = self.builder.strands[self.strand_id]
.domains
.last()
.unwrap()
.helix;
let (start, end, right) = if to < self.x.unwrap() {
let n = (to, self.x.unwrap(), false);
self.x = Some(to);
n
} else {
let n = (self.x.unwrap() + 1, to + 1, true);
self.x = Some(to);
n
};
let domain = Domain {
helix,
start,
end,
right,
label: None,
sequence: None,
};
self.builder.strands[self.strand_id].domains.push(domain);
self
}
pub fn cross<H: Into<HelixId>>(self, h: H) -> Self {
let x = self.x.unwrap();
self.cross_to(h, x)
}
pub fn cross_to<H: Into<HelixId>>(mut self, h: H, start: isize) -> Self {
let helix = h.into().0;
if helix >= self.builder.helices.len() as isize {
panic!("Crossing to an undefined helix")
}
self.builder.strands[self.strand_id].domains.push(Domain {
helix,
start,
end: start,
right: true,
label: None,
sequence: None,
});
self.x0 = Some(start);
self.x = Some(start);
self
}
pub fn hflip(self, x: isize) -> Self {
for dom in self.builder.strands[self.strand_id].domains.iter_mut() {
let start = dom.start;
dom.start = x - dom.end;
dom.end = x - start;
}
self.builder.strands[self.strand_id].domains.reverse();
self
}
pub fn vflip<H: Into<HelixId>>(self, y: H) -> Self {
let y = y.into();
for dom in self.builder.strands[self.strand_id].domains.iter_mut() {
assert!(y.0 >= dom.helix);
dom.helix = 2 * y.0 - dom.helix
}
self
}
pub fn translate(self, x: isize, y: isize) -> Self {
use std::convert::TryFrom;
for dom in self.builder.strands[self.strand_id].domains.iter_mut() {
dom.helix = usize::try_from(dom.helix as isize + y).unwrap() as isize;
dom.start += x;
dom.end += x;
}
self
}
pub fn cycle(self) -> () {
let helix = self.builder.strands[self.strand_id].domains[0].helix;
let start = self.builder.strands[self.strand_id].domains[0].start;
let id = self.strand_id;
self.builder.strands[id].cyclic = true;
self.cross(helix).to(start);
}
}
fn sequence<StrandLabel, DomainLabel>(
positions: &HashMap<(usize, isize, bool), char>,
strand: &Strand<StrandLabel, DomainLabel>,
) -> String {
let mut seq = String::new();
for dom in strand.domains.iter() {
if let Some(ref s) = dom.sequence {
seq.push_str(s);
continue;
}
assert!(dom.helix >= 0);
for pos in dom.iter() {
seq.push(sequences::complement(
*positions
.get(&(dom.helix as usize, pos, !dom.right))
.unwrap(),
))
}
}
seq
}
pub mod transforms {
use super::Domain;
pub fn rotate<Label>(domains: &mut [Domain<Label>]) {
let max_helix = domains.iter().map(|x| x.helix).max().unwrap();
for dom in domains.iter_mut() {
let start = dom.start;
dom.start = -dom.end;
dom.end = -start;
dom.helix = max_helix - dom.helix;
dom.right = !dom.right;
}
}
pub fn v_flip<Label>(domains: &mut [Domain<Label>], max_helix: isize) {
for dom in domains.iter_mut() {
dom.helix = max_helix - dom.helix;
}
}
pub fn reverse<Label>(domains: &mut [Domain<Label>]) {
for dom in domains.iter_mut() {
dom.right = !dom.right;
}
}
pub fn h_flip<Label>(domains: &mut [Domain<Label>]) {
for dom in domains.iter_mut() {
let start = dom.start;
dom.start = -dom.end;
dom.end = -start;
}
}
pub fn translate<Label>(domains: &mut [Domain<Label>], dh: isize, dx: isize) {
use std::convert::TryFrom;
for dom in domains.iter_mut() {
dom.helix = usize::try_from(dom.helix as isize + dh).unwrap() as isize;
dom.start += dx;
dom.end += dx;
}
}
}
impl<StrandLabel, DomainLabel> Design<StrandLabel, DomainLabel> {
fn positions(&self) -> HashMap<(usize, isize, bool), char> {
let mut positions = HashMap::new();
for s in self.strands.iter() {
if let Some(ref seq) = s.sequence {
let mut seq = seq.chars();
for dom in s.domains.iter() {
if dom.helix < 0 {
continue;
}
for pos in dom.iter() {
let c = seq.next().unwrap();
positions.insert((dom.helix as usize, pos, dom.right), c);
}
}
}
}
positions
}
pub fn sequences(&self) -> Vec<String> {
let positions = self.positions();
let mut sequences = Vec::new();
for s in self.strands.iter() {
if s.sequence.is_none() {
sequences.push(sequence(&positions, s))
}
}
sequences
}
}
#[cfg(feature = "excel")]
impl<StrandLabel: Ord + std::fmt::Display, DomainLabel>
Design<StrandLabel, DomainLabel>
{
pub fn make_plates_96<
H: FnMut(&Strand<StrandLabel, DomainLabel>) -> usize,
F: FnMut(&Strand<StrandLabel, DomainLabel>) -> Option<bool>,
G: FnMut(usize) -> String,
>(
&mut self,
file: &str,
mut seq_class: H,
mut filter: F,
mut format: G,
) {
self.strands.sort_by(|a, b| seq_class(&a).cmp(&seq_class(b)));
let mut current_plate = 0;
let mut current_class = 0;
let mut plate = Plate96::new();
let mut plates = vec![Vec::new()];
let positions = self.positions();
for s in self.strands.iter() {
let class = seq_class(s);
if class != current_class && plate.row > 0 {
plate.incr_col();
}
if plate.n != current_plate {
plates.push(Vec::new());
current_plate = plate.n;
}
if let Some(x) = filter(s) {
if x && (plate.row != 0 || plate.column != 0) {
plate.incr_plate();
plates.push(Vec::new());
current_plate = plate.n;
}
plates.last_mut().unwrap().push((
plate.column,
plate.row,
&s.label,
sequence(&positions, s),
));
plate.incr()
}
current_class = class;
}
use simple_excel_writer::*;
let mut wb = Workbook::create(file);
for (i, seqs) in plates.iter().enumerate() {
let mut sheet = wb.create_sheet(&format(i));
sheet.add_column(Column { width: 30.0 });
sheet.add_column(Column { width: 30.0 });
sheet.add_column(Column { width: 30.0 });
wb.write_sheet(&mut sheet, |sw| {
sw.append_row(row!["Well Position", "Name", "Sequence"])?;
for &(col, row, label, ref seq) in seqs.iter() {
let row = (b'A' + row as u8) as char;
let label = if let Some(ref label) = label {
format!("{}", label)
} else {
format!("{}{}", row, col + 1)
};
sw.append_row(row![
format!("{}{}", row, col + 1).as_str(),
label.as_str(),
seq.as_str()
])?
}
Ok(())
})
.unwrap()
}
wb.close().unwrap();
}
}
#[cfg(feature = "excel")]
#[derive(Debug, Copy, Clone)]
pub struct Plate96 {
pub n: usize,
pub column: usize,
pub row: usize,
}
#[cfg(feature = "excel")]
impl Plate96 {
pub fn new() -> Self {
Plate96 {
n: 0,
column: 0,
row: 0,
}
}
pub fn incr(&mut self) {
self.row += 1;
if self.row >= 8 {
self.incr_col()
}
}
pub fn incr_col(&mut self) {
self.column += 1;
self.row = 0;
if self.column >= 12 {
self.incr_plate()
}
}
pub fn incr_plate(&mut self) {
self.column = 0;
self.row = 0;
self.n += 1;
}
}