use log::{info};
use crate::arena::Options;
use crate::arena::ArenaTree;
use crate::arena::Event;
use crate::arena::BLOCK;
use crate::arena::PIPEBLOCK;
use svg::Document;
use svg::node::element::Path;
use svg::node::element::Circle;
use svg::node::element::Style;
use svg::node::Text;
use svg::node::element::Element;
use svg::node::element::path::Data;
use svg::Node;
use random_color::{Color,RandomColor,Luminosity};
const GTHICKNESS: usize = 3;
const STHICKNESS: usize = 6;
const SQUARESIZE: f32 = 6.0;
pub fn draw_tree (tree: &mut ArenaTree<String>, name: String, options: &Options) {
info!("[draw_tree] Drawing tree...");
let gene_color = "blue";
let largest_x = tree.get_largest_x() * 1.0 + 0.0 ;
let largest_y = tree.get_largest_y() * 1.0 + 0.0 ;
let smallest_x = tree.get_smallest_x() * 1.0 + 0.0 ;
let smallest_y = tree.get_smallest_y() * 1.0 + 0.0 ;
let width_svg = largest_x - smallest_x + 0.0;
let width_svg = width_svg * 1.0;
let height_svg = largest_y - smallest_y + 2.0 * BLOCK;
let height_svg = height_svg * 1.0;
let x_viewbox = smallest_x - 0.0 ;
let y_viewbox = smallest_y - 0.0;
let mut document = Document::new()
.set("width",height_svg + BLOCK )
.set("height",width_svg + BLOCK )
.set("viewBox", (x_viewbox,y_viewbox,height_svg + 2.0 * BLOCK ,width_svg + 2.0 * BLOCK ));
let style = Style::new(".vert { font: italic 12px serif; fill: green; }");
document.append(style);
let mut g = Element::new("g");
for index in &tree.arena {
let _parent = match index.parent {
Some(p) => {
let n = &tree.arena[p];
let chemin = match index.is_a_transfert {
true => {get_chemin_carre(index.x,index.y,n.x,n.y,gene_color.to_string(),true)},
false => {get_chemin_carre(index.x,index.y,n.x,n.y,gene_color.to_string(),false)},
};
g.append(chemin);
0
},
None => {-1},
};
let event = &index.e;
match event {
Event::Leaf => g.append(get_carre(index.x,index.y,2.0,"red".to_string())),
Event::Duplication => g.append(get_carre(index.x,index.y,SQUARESIZE,"blue".to_string())),
Event::Loss => {
let mut cross = get_cross(index.x,index.y,4.0,"blue".to_string());
cross.assign("transform","rotate(45 ".to_owned()+&index.x.to_string()
+ " "+&index.y.to_string()+")");
g.append(cross);
},
Event::BranchingOut => {
let mut diamond = get_carre(index.x,index.y,12.0,"orange".to_string());
diamond.assign("transform","rotate(45 ".to_owned() + &index.x.to_string()
+ " " + &index.y.to_string() + ")" );
g.append(diamond);
},
_ => g.append(get_circle(index.x,index.y,3.0,"blue".to_string())),
};
match index.is_a_transfert {
true => { g.append(get_triangle(index.x,index.y - 6.0,12.0,"yellow".to_string())) },
false => {},
};
let mut element = Element::new("text");
element.assign("x", index.x-5.0);
element.assign("y", index.y+10.0);
element.assign("class", "vert");
let txt = Text::new(&index.name);
element.append(txt);
element.assign("transform","rotate(90 ".to_owned()+&(index.x - 5.0).to_string()
+ ","+&(index.y + 10.0).to_string()+")");
match tree.is_leaf(index.idx) {
true => g.append(element),
_ => match options.gene_internal {
true => g.append(element),
false => {},
},
};
}
let mut transfo: String = "translate( ".to_owned();
transfo.push_str(&( x_viewbox).to_string());
transfo.push_str(" ");
transfo.push_str(&((width_svg + y_viewbox)).to_string());
transfo.push_str(") rotate(-90 0 0 ) ");
g.assign("transform",transfo);
document.append(g);
svg::save(name, &document).unwrap();
}
pub fn draw_sptree_gntrees (
sp_tree: &mut ArenaTree<String>,
gene_trees:&mut std::vec::Vec<ArenaTree<String>>,
name: String, options: &Options
) {
let largest_x = sp_tree.get_largest_x() * 1.0 + 0.0 ;
let largest_y = sp_tree.get_largest_y() * 1.0 + 0.0 ;
let smallest_x = sp_tree.get_smallest_x() * 1.0 + 0.0 ;
let smallest_y = sp_tree.get_smallest_y() * 1.0 + 0.0 ;
let width_svg = largest_x - smallest_x+ 1.0 * BLOCK;
let width_svg = width_svg * 1.0;
let height_svg = largest_y - smallest_y + 2.0 * BLOCK;
let height_svg = height_svg * 1.0;
let x_viewbox = smallest_x - 0.0 ;
let y_viewbox = smallest_y - 0.0;
let mut document = Document::new()
.set("width",height_svg )
.set("height",width_svg )
.set("viewBox", (x_viewbox,y_viewbox,height_svg + 2.0 *BLOCK ,width_svg + 2.0 *BLOCK ));
let style = Style::new(".vert { font: 12px serif; fill: green; }");
document.append(style);
let style = Style::new(".jaune { font: italic 12px serif; fill: orange; }");
document.append(style);
let mut g = Element::new("g");
for index in &sp_tree.arena {
match index.parent {
Some(p) => {
let n = &sp_tree.arena[p];
let chemin = get_chemin_sp(index.x, index.y,
index.width/2.0, index.height/2.0,
n.x, n.y,
n.width/2.0, n.height/2.0);
g.append(chemin);
if sp_tree.is_leaf(index.idx) {
let chemin = close_chemin_sp(index.x, index.y,
index.width/2.0, index.height/2.0);
g.append(chemin);
}
},
None => {},
};
let mut element = Element::new("text");
match sp_tree.is_leaf(index.idx) {
true => {
element.assign("x", index.x-15.0);
element.assign("y", index.y - index.width /2.0 - 10.0);
element.assign("class", "jaune");
let txt = Text::new(&index.name);
element.append(txt);
element.assign("transform","rotate(90 ".to_owned() + &index.x.to_string()
+ "," + &index.y.to_string() + ")" );
g.append(element);
},
false => {
match options.species_internal {
true => {
element.assign("x", index.x+15.0);
element.assign("y", index.y+20.0);
element.assign("class", "jaune");
let txt = Text::new(&index.name);
element.append(txt);
element.assign("transform","rotate(90 ".to_owned()+&index.x.to_string()
+ "," + &index.y.to_string() + ")" );
g.append(element);
},
false => {},
};
},
};
}
let nb_gntree = gene_trees.len();
let mut idx_rcgen = 0;
loop {
let base_couleur = match &idx_rcgen % 6 {
5 => Color::Orange,
0 => Color::Blue,
1 => Color::Purple,
2 => Color::Green,
3 => Color::Red,
4 => Color::Yellow,
_ => Color::Monochrome,
};
let gene_color = RandomColor::new()
.hue(base_couleur)
.luminosity(Luminosity::Bright)
.alpha(1.0)
.to_rgb_string();
let style = Style::new(".gene_".to_owned()+&idx_rcgen.to_string()
+ " { font: 10px serif; fill:" + &gene_color.to_string() + "; }" );
document.append(style);
for index in &gene_trees[idx_rcgen].arena {
match index.parent {
Some(p) => {
let n = &gene_trees[idx_rcgen].arena[p];
let chemin = match index.is_a_transfert {
true => {
match n.e {
Event::BranchingOut => get_chemin_transfer(index.x,index.y,
n.x,n.y,
gene_color.to_string()
),
Event::BifurcationOut => get_chemin_transfer(index.x,index.y,
n.x,n.y,
gene_color.to_string()
),
_ => panic!("Wrong recPhyloXML feature.
The father node should be BranchingOut or
BifurcationOut, but I found a {:?}\n{:?}",n.e,n),
}
},
false => get_chemin_carre(index.x,index.y,n.x,n.y ,gene_color.to_string(),false),
};
g.append(chemin);
},
None => {
let mut element = Element::new("text");
element.assign("x", index.x+10.0);
element.assign("y", index.y+0.0);
element.assign("class", "gene_".to_owned() + &idx_rcgen.to_string());
let txt = Text::new(&idx_rcgen.to_string());
element.append(txt);
element.assign("transform","rotate(90 ".to_owned() + &index.x.to_string()
+ "," + &index.y.to_string() + ")" );
g.append(element);
},
};
let event = &index.e;
match event {
Event::Leaf => g.append(get_carre(index.x,index.y,1.0,"red".to_string())),
Event::Duplication => g.append(get_carre(index.x,index.y,SQUARESIZE,
gene_color.to_string())),
Event::Loss => {
let mut cross = get_cross(index.x,index.y,2.0,gene_color.to_string());
cross.assign("transform","rotate(45 ".to_owned() + &index.x.to_string()
+ " " + &index.y.to_string() + ")" );
g.append(cross);
},
Event::BranchingOut => {
let mut diamond = get_carre(index.x,index.y,4.0,gene_color.to_string());
diamond.assign("transform","rotate(45 ".to_owned() + &index.x.to_string()
+ " " + &index.y.to_string() + ")" );
g.append(diamond);
},
Event::BifurcationOut => g.append(get_carre(index.x,index.y,5.0,
"yellow".to_string())),
_ => g.append(get_circle(index.x,index.y,3.0,gene_color.to_string())),
};
match event {
Event::Leaf => {
let mut element = Element::new("text");
element.assign("x", index.x+10.0);
element.assign("y", index.y+0.0);
element.assign("class", "gene_".to_owned()+&idx_rcgen.to_string());
let txt = Text::new(&index.name);
element.append(txt);
element.assign("transform","rotate(90 ".to_owned() + &index.x.to_string()
+ "," + &index.y.to_string() + ")" );
g.append(element);
},
_ => {
match options.gene_internal {
true => {
let mut element = Element::new("text");
element.assign("x", index.x+10.0);
element.assign("y", index.y+0.0);
element.assign("class", "gene_".to_owned() + &idx_rcgen.to_string());
let txt = Text::new(&index.name);
element.append(txt);
element.assign("transform","rotate(90 ".to_owned()
+ &index.x.to_string() + "," + &index.y.to_string()+")");
g.append(element);
},
false => {},
}
},
}
}
idx_rcgen += 1;
if idx_rcgen == nb_gntree {
break;
}
}
let mut transfo: String = "translate( ".to_owned();
transfo.push_str(&( x_viewbox).to_string());
transfo.push_str(" ");
transfo.push_str(&((width_svg + y_viewbox)).to_string());
transfo.push_str(") rotate(-90 0 0 ) ");
g.assign("transform",transfo);
document.append(g);
svg::save(name, &document).unwrap();
}
#[allow(dead_code)]
pub fn get_cadre (x: f32, y:f32,w:f32,h:f32, c:String) -> Path {
let data = Data::new()
.move_to((x , y))
.line_by((w, 0.0 ))
.line_by((0.0, h))
.line_by((-w, 0.0))
.close();
let path = Path::new()
.set("fill", "none")
.set("stroke", c)
.set("stroke-width", 3)
.set("d", data);
path
}
pub fn get_carre (x: f32, y:f32, s:f32, c:String) -> Path {
let data = Data::new()
.move_to((x*1.0 -s*0.5 , y*1.0 -s*0.5))
.line_by((0, s))
.line_by((s, 0))
.line_by((0, -s))
.close();
let fill = c.clone();
let path = Path::new()
.set("fill", fill)
.set("stroke", c)
.set("stroke-width", 3)
.set("d", data);
path
}
pub fn get_triangle (x: f32, y:f32, s:f32, c:String) -> Path {
let data = Data::new()
.move_to((x*1.0, y*1.0))
.line_by((-s, -s))
.line_by((2.0 * s, 0))
.close();
let fill = c.clone();
let path = Path::new()
.set("fill", fill)
.set("stroke", c)
.set("stroke-width", 1)
.set("d", data);
path
}
pub fn get_circle (x: f32, y:f32, r:f32, c:String) -> Circle {
let fill = c.clone();
let circle = Circle::new()
.set("cx", x)
.set("cy", y)
.set("r", r)
.set("fill", fill)
.set("stroke", c)
.set("stroke-width", 1);
circle
}
pub fn get_cross (x: f32, y:f32, s:f32, c:String) -> Path {
let data = Data::new()
.move_to((x*1.0 , y*1.0 -s*2.0))
.line_by((0, s*4.0))
.move_to((x*1.0 -s*2.0, y*1.0 ))
.line_by((s*4.0, 0));
let fill = c.clone();
let path = Path::new()
.set("fill", fill)
.set("stroke", c)
.set("stroke-width", s*1.0)
.set("d", data);
path
}
#[allow(dead_code)]
pub fn get_chemin_semisquare (x1: f32, y1:f32,x2: f32, y2:f32) -> Path {
let data = Data::new()
.move_to((x1*1.0, y1*1.0))
.line_to((x1*1.0, (y1+y2)*1.0/2.0))
.line_to((x2*1.0, (y1+y2)*1.0/2.0))
.line_to((x2*1.0, y2*1.0));
let path = Path::new()
.set("fill", "none")
.set("stroke", "blue")
.set("stroke-width", GTHICKNESS)
.set("d", data);
path
}
pub fn get_chemin_carre (x1: f32, y1:f32,x2: f32, y2:f32, c:String, stroke:bool) -> Path {
let data = Data::new()
.move_to((x1*1.0, y1*1.0))
.line_to((x1*1.0, y2*1.0))
.line_to((x2*1.0, y2*1.0));
let path = Path::new()
.set("fill", "none")
.set("stroke", c)
.set("stroke-width", GTHICKNESS);
let path = match stroke {
true => path.set("stroke-dasharray","1, 1"),
false => path,
};
let path = path.set("d", data);
path
}
pub fn get_chemin_transfer (x1: f32, y1:f32,x2: f32, y2:f32, c:String) -> Path {
let initial_y1 = y1 ;
let y1 = y1 - PIPEBLOCK;
let bez_y = BLOCK;
let controle_x = (x1 + x2) / 2.0 ;
let controle_y = (y1 + y2) / 2.0 - bez_y ;
let data = "M".to_owned() + &x1.to_string() +" " + &initial_y1.to_string()
+" L "+ &x1.to_string() + " " + &y1.to_string();
let data = data.to_owned()+ "M" + &x1.to_string() + " "
+ &(initial_y1- PIPEBLOCK / 4.0).to_string() + "L "
+ &(x1 - PIPEBLOCK / 4.0 ).to_string() + " "
+ &(initial_y1 - PIPEBLOCK / 2.0 ).to_string();
let data = data.to_owned()+ "M"+&x1.to_string() + " "
+ &(initial_y1- PIPEBLOCK / 4.0).to_string() + "L "
+ &(x1 + PIPEBLOCK / 4.0 ).to_string() + " "
+ &(initial_y1 - PIPEBLOCK / 2.0 ).to_string();
let data = data.to_owned() + "M" + &x1.to_string() + " " + &y1.to_string()
+ " Q " + &controle_x.to_string() + " " + &controle_y.to_string()
+ " " + &x2.to_string() + " " + &y2.to_string();
let path = Path::new()
.set("fill", "none")
.set("stroke", c)
.set("stroke-width", GTHICKNESS)
.set("stroke-dasharray","1, 1")
.set("d",data);
path
}
#[allow(dead_code)]
pub fn get_chemin_simple (x1: f32, y1:f32,x2: f32, y2:f32) -> Path {
let data = Data::new()
.move_to((x1*1.0, y1*1.0))
.line_to((x2*1.0, y2*1.0));
let path = Path::new()
.set("fill", "none")
.set("stroke", "blue")
.set("stroke-width", 1)
.set("d", data);
path
}
pub fn get_chemin_sp (x1: f32, y1:f32, width1:f32, height1:f32, x2: f32, y2:f32,
width2:f32, height2:f32 ) -> Path {
if x1 < x2 {
let data = Data::new()
.move_to((x1 - width1, y1 - height1 + (STHICKNESS / 2) as f32))
.line_to((x1 - width1, y2 - height2))
.line_to((x2 - width2, y2 - height2))
.move_to((x1 + width1, y1 - height1 + (STHICKNESS / 2) as f32 ))
.line_to((x1 + width1, y2 + height2))
.line_to((x2, y2 + height2));
let path = Path::new()
.set("fill", "none")
.set("stroke", "pink")
.set("stroke-width", STHICKNESS)
.set("d", data);
path
}
else {
let data = Data::new()
.move_to((x1 + width1, y1 - height1 + (STHICKNESS / 2) as f32 ))
.line_to((x1 + width1, y2 - height2))
.line_to((x2 + width2, y2 - height2))
.move_to((x1 - width1, y1 - height1 + (STHICKNESS / 2) as f32))
.line_to((x1 - width1, y2 + height2))
.line_to((x2, y2 + height2));
let path = Path::new()
.set("fill", "none")
.set("stroke", "pink")
.set("stroke-width", STHICKNESS)
.set("d", data);
path
}
}
pub fn close_chemin_sp (x1: f32, y1:f32, width1:f32, height1:f32 ) -> Path {
let data = Data::new()
.move_to((x1 - width1, y1 - height1))
.line_to((x1 - width1, y1 + 2.0 * height1))
.line_to((x1 + width1, y1 + 2.0 * height1))
.line_to((x1 + width1, y1 - height1));
let path = Path::new()
.set("fill", "none")
.set("stroke", "pink")
.set("stroke-width", STHICKNESS)
.set("d", data);
path
}