cadnano_format/
lib.rs

1//! Converter from the CaDNAno format to strands, and back.
2extern crate palette;
3extern crate serde;
4extern crate serde_json;
5#[macro_use]
6extern crate serde_derive;
7
8use palette::{Hsl, Srgb};
9use std::collections::HashMap;
10use std::collections::HashSet;
11mod cadnano;
12pub use cadnano::*;
13
14#[derive(Debug)]
15pub enum Error {
16    IO(std::io::Error),
17    Json(serde_json::Error),
18}
19
20impl std::convert::From<std::io::Error> for Error {
21    fn from(e: std::io::Error) -> Self {
22        Error::IO(e)
23    }
24}
25
26impl std::convert::From<serde_json::Error> for Error {
27    fn from(e: serde_json::Error) -> Self {
28        Error::Json(e)
29    }
30}
31
32pub fn color_rgb(y: usize, x: usize) -> Srgb {
33    Hsl::new(((y * 31 + x * 3) % 360) as f32, 1., 0.4).into()
34}
35
36pub fn color(y: usize, x: usize) -> isize {
37    let rgb = color_rgb(y, x);
38    let (r, g, b) = rgb.into_components();
39    let r = (r * 255.).round() as isize;
40    let g = (g * 255.).round() as isize;
41    let b = (b * 255.).round() as isize;
42    (((r << 8) | g) << 8) | b
43}
44
45#[derive(Debug)]
46pub struct Point {
47    pub helix: isize,
48    pub x: isize,
49}
50
51#[derive(Debug)]
52pub struct Strand {
53    pub path: Vec<Point>,
54}
55
56#[derive(Debug)]
57pub struct Strands {
58    pub scaffolds: Vec<Strand>,
59    pub staples: Vec<Strand>,
60    pub edits: Vec<(Point, isize)>,
61}
62
63
64pub enum Lattice {
65    Square,
66    Hexagonal,
67}
68
69impl Cadnano {
70
71    fn follow_scaffold(
72        &self,
73        points: &mut HashSet<(isize, isize)>,
74        nums: &HashMap<isize, usize>,
75        helix0: isize,
76        x0: isize,
77    ) -> Strand {
78        let mut path = Vec::new();
79        let (mut helix, mut x) = (helix0, x0);
80        // Follow backwards.
81        while helix != -1 {
82            let (a, b, _, _) = self.vstrands[*nums.get(&helix).unwrap() as usize].scaf[x as usize];
83            let n = path.len();
84            if n < 2 {
85                path.push(Point { helix, x });
86            } else {
87                if path[n-1].helix == path[n-2].helix && path[n-1].helix == helix {
88                    path.last_mut().unwrap().x = x
89                } else {
90                    path.push(Point { helix, x });
91                }
92            }
93            points.remove(&(helix, x));
94            helix = a;
95            x = b;
96        }
97        path.reverse();
98        path.pop();
99        println!("scaffold reversed: {:?}", path);
100        helix = helix0;
101        x = x0;
102        while helix != -1 {
103            let (_, _, c, d) = self.vstrands[*nums.get(&helix).unwrap() as usize].scaf[x as usize];
104            let n = path.len();
105            if n < 2 {
106                path.push(Point { helix, x });
107            } else {
108                if path[n-1].helix == path[n-2].helix && path[n-1].helix == helix {
109                    path.last_mut().unwrap().x = x
110                } else {
111                    path.push(Point { helix, x });
112                }
113            }
114            points.remove(&(helix, x));
115            helix = c;
116            x = d;
117        }
118        Strand {
119            path
120        }
121    }
122
123    fn follow_staple(
124        &self,
125        points: &mut HashSet<(isize, isize)>,
126        nums: &HashMap<isize, usize>,
127        helix0: isize,
128        x0: isize,
129    ) -> Strand {
130        let mut path = Vec::new();
131        let (mut helix, mut x) = (helix0, x0);
132        // Follow backwards.
133        while helix != -1 {
134            let (a, b, _, _) = self.vstrands[*nums.get(&helix).unwrap() as usize].stap[x as usize];
135            let n = path.len();
136            if n < 2 {
137                path.push(Point { helix, x });
138            } else {
139                if path[n-1].helix == path[n-2].helix && path[n-1].helix == helix {
140                    path.last_mut().unwrap().x = x
141                } else {
142                    path.push(Point { helix, x });
143                }
144            }
145            points.remove(&(helix, x));
146            helix = a;
147            x = b;
148        }
149        println!("reversed staple: {:?}", path);
150        path.reverse();
151        path.pop();
152        helix = helix0;
153        x = x0;
154        while helix != -1 {
155            let (_, _, c, d) = self.vstrands[*nums.get(&helix).unwrap() as usize].stap[x as usize];
156            let n = path.len();
157            if n < 2 {
158                path.push(Point { helix, x });
159            } else {
160                if path[n-1].helix == path[n-2].helix && path[n-1].helix == helix {
161                    path.last_mut().unwrap().x = x
162                } else {
163                    path.push(Point { helix, x });
164                }
165            }
166            points.remove(&(helix, x));
167            helix = c;
168            x = d;
169        }
170        Strand {
171            path
172        }
173    }
174
175    pub fn to_strands(&self) -> Strands {
176        let mut scaf_points = HashSet::new();
177        let mut stap_points = HashSet::new();
178        let mut nums = HashMap::new();
179        for (vs, vstrand) in self.vstrands.iter().enumerate() {
180            nums.insert(vstrand.num, vs);
181            for (x, st) in vstrand.scaf.iter().enumerate() {
182                if *st != (-1, -1, -1, -1) {
183                    scaf_points.insert((vstrand.num, x as isize));
184                }
185            }
186            for (x, st) in vstrand.stap.iter().enumerate() {
187                if *st != (-1, -1, -1, -1) {
188                    stap_points.insert((vstrand.num, x as isize));
189                }
190            }
191        }
192        let mut scaffolds = Vec::new();
193        loop {
194            let (num, x) = {
195                let mut it = scaf_points.iter();
196                if let Some(&(num, x)) = it.next() {
197                    (num, x)
198                } else {
199                    break
200                }
201            };
202            scaffolds.push(self.follow_scaffold(&mut scaf_points, &nums, num, x))
203        }
204        let mut staples = Vec::new();
205        loop {
206            let (num, x) = {
207                let mut it = stap_points.iter();
208                if let Some(&(num, x)) = it.next() {
209                    (num, x)
210                } else {
211                    break
212                }
213            };
214            staples.push(self.follow_staple(&mut stap_points, &nums, num, x))
215        }
216        let mut edits = Vec::new();
217        for (h, vs) in self.vstrands.iter().enumerate() {
218            for &x in vs.skip.iter().chain(vs.loop_.iter()) {
219                if x != 0 {
220                    edits.push((
221                        Point { helix: h as isize, x: x as isize },
222                        x
223                    ))
224                }
225            }
226        }
227        Strands {
228            scaffolds,
229            staples,
230            edits,
231        }
232    }
233
234    pub fn from_strands(
235        name: String,
236        lattice: Lattice,
237        scaffolds: &[Strand],
238        staples: &[Strand],
239        edits: &[(Point, isize)],
240    ) -> Cadnano {
241        let ref p0 = scaffolds[0].path[0];
242        let (min_x, max_x, min_y, max_y) = scaffolds
243            .iter().flat_map(|s| s.path.iter())
244            .chain(staples.iter().flat_map(|s| s.path.iter()))
245            .fold(
246                (p0.x, p0.x, p0.helix, p0.helix),
247                |(min_x, max_x, min_y, max_y), p| {
248                    (
249                        std::cmp::min(min_x, p.x),
250                        std::cmp::max(max_x, p.x),
251                        std::cmp::min(min_y, p.helix),
252                        std::cmp::max(max_y, p.helix),
253                    )
254                },
255            );
256        let min_x = min_x;
257        let columns = (max_x - min_x + 1) as usize;
258        let mut vstrands = Vec::new();
259
260        // Now convert that to VStrand.
261        let columns = match lattice {
262            Lattice::Square => {
263                let mut cols = (columns / 32) * 32;
264                while cols % 21 == 0 || cols < columns {
265                    cols += 32
266                }
267                cols
268            }
269            Lattice::Hexagonal => {
270                let mut cols = (columns / 21) * 21;
271                while cols % 32 == 0 || cols < columns {
272                    cols += 21
273                }
274                cols
275            }
276        };
277        for y in min_y..=max_y {
278            // (num, (h, row)) in helices.iter().zip(events_.iter()).enumerate() {
279            let num = y - min_y;
280            let stap: Vec<(isize, isize, isize, isize)> = vec![(-1, -1, -1, -1); columns];
281            let scaf = stap.clone();
282
283            vstrands.push(VStrand {
284                stap_colors: vec![],
285                num,
286                scaf_loop: vec![],
287                stap,
288                skip: vec![0; columns as usize],
289                scaf,
290                stap_loop: vec![],
291                col: 1,
292                loop_: vec![0; columns as usize],
293                row: num as isize + 1,
294            })
295        }
296
297        for (is_scaffold, path) in
298            scaffolds.iter().map(|x| (true, x))
299            .chain(staples.iter().map(|x| (false, x)))
300        {
301            let (mut x0, mut y0): (isize, isize) = (-1, -1);
302            let it0 = path.path.iter().map(|p| Point {
303                helix: p.helix - min_y,
304                x: p.x - min_x,
305            });
306            let mut it1 = path.path.iter().map(|p| Point {
307                helix: p.helix - min_y,
308                x: p.x - min_x,
309            });
310            it1.next();
311            for (p0, p1) in it0.zip(it1) {
312                if p0.helix == p1.helix {
313                    if p0.x < p1.x {
314                        for x in p0.x..p1.x {
315                            if is_scaffold {
316                                vstrands[p0.helix as usize].scaf[x as usize] =
317                                    (y0, x0, p0.helix, x + 1);
318                            } else {
319                                vstrands[p0.helix as usize].stap[x as usize] =
320                                    (y0, x0, p0.helix, x + 1);
321                            }
322                            y0 = p0.helix;
323                            x0 = x;
324                        }
325                        if is_scaffold {
326                            vstrands[p1.helix as usize].scaf[p1.x as usize] = (y0, x0, -1, -1);
327                        } else {
328                            vstrands[p1.helix as usize].stap[p1.x as usize] = (y0, x0, -1, -1);
329                        }
330                    } else {
331                        for x in ((p1.x + 1)..=p0.x).rev() {
332                            if is_scaffold {
333                                vstrands[p0.helix as usize].scaf[x as usize] =
334                                    (y0, x0, p1.helix, x - 1);
335                            } else {
336                                vstrands[p0.helix as usize].stap[x as usize] =
337                                    (y0, x0, p1.helix, x - 1);
338                            }
339                            y0 = p0.helix;
340                            x0 = x;
341                        }
342                        if is_scaffold {
343                            vstrands[p1.helix as usize].scaf[p1.x as usize] = (y0, x0, -1, -1);
344                        } else {
345                            vstrands[p1.helix as usize].stap[p1.x as usize] = (y0, x0, -1, -1);
346                        }
347                    }
348                } else {
349                    // Helix change.
350                    if is_scaffold {
351                        vstrands[p0.helix as usize].scaf[p0.x as usize] = (y0, x0, p1.helix, p1.x);
352                    } else {
353                        vstrands[p0.helix as usize].stap[p0.x as usize] = (y0, x0, p1.helix, p1.x);
354                    }
355                    x0 = p0.x;
356                    y0 = p0.helix;
357                }
358            }
359        }
360
361        for &(ref p, ref n) in edits.iter() {
362            println!("{:?}", p);
363            if *n > 0 {
364                vstrands[p.helix as usize].loop_[p.x as usize] = *n
365            } else if *n < 0 {
366                vstrands[p.helix as usize].skip[p.x as usize] = *n
367            }
368        }
369        Cadnano { name, vstrands }
370    }
371}
372
373#[test]
374fn test_cad() {
375    let scaf = vec![Strand {
376        path: vec![
377            Point { x: 0, helix: 0 },
378            Point { x: 20, helix: 0 },
379            Point { x: 20, helix: 1 },
380            Point { x: 0, helix: 1 },
381        ],
382    }];
383    let staples = vec![Strand {
384        path: vec![
385            Point { x: 20, helix: 0 },
386            Point { x: 0, helix: 0 },
387            Point { x: 0, helix: 1 },
388            Point { x: 20, helix: 1 },
389        ],
390    }];
391    let cad = Cadnano::from_strands("blabla".to_string(), Lattice::Square, &scaf, &staples, &[(Point { x: 4, helix: 0 }, 1), (Point { x: 4, helix: 1 }, -1)]);
392    cad.to_file("/tmp/test.json").unwrap();
393    println!("{:?}", cad.to_strands());
394}