Created
March 10, 2020 10:07
-
-
Save gsurrel/d3b3903260c619814969b9799642a129 to your computer and use it in GitHub Desktop.
Barometer Sheet Generation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Exercice on Rust programming: | |
// Draw an SVG file to replicate this kind of barometric measurement sheet: | |
// https://www.naudet.com/diagrammes-6mm-c2x15057112 | |
use svg::Document; | |
use svg::node::element::{Path, Text, Circle, Rectangle, Line}; | |
use svg::node::element::path::Data; | |
fn main() { | |
println!("Génération de feuille barométrique."); | |
// All values in millimeters | |
let paper_size = (306, 90); | |
let arm_length = 185.; // that is the circle radius | |
let day_width = 279.5 / 7.; | |
let color = "#ff9500"; // #ff9500 is orange-ish | |
// Create SVG document | |
let header_offset = 50.; // Graph is 80mm, header 10mm, so offset is half a graph plus a full header | |
let mut document = Document::new() | |
.set("viewBox", (0, -header_offset, paper_size.0, paper_size.1)) // Viewbox centered on the "y=0" line | |
.set("width", format!("{}mm", paper_size.0)) | |
.set("height", format!("{}mm", paper_size.1)); | |
// Draw days | |
let days = vec!["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche", ""]; // One additional day for paper overflow | |
for (i, day) in days.into_iter().enumerate() { | |
let midday_x = i as f64 * day_width + day_width / 2.; | |
// Circles of hours | |
for hour in (0..24).step_by(1) { | |
let width = if hour == 0 { 1. } else if hour == 12 { 0.5 } else if hour % 2 == 0 { 0.25 } else { 0.1 }; | |
let hour_circle = Circle::new() | |
.set("cx", midday_x + ((hour - 12) as f64) / 24. * day_width + arm_length) | |
.set("cy", 0) | |
.set("r", arm_length) | |
.set("style", format!("fill: none; stroke: {}; stroke-width: {}", color, width)); | |
document = document.add(hour_circle); | |
} | |
// Lines of pressure | |
let central_pressure = 755; | |
let start_pressure = 715; | |
let end_pressure = 795; | |
let range_mm = 80.; | |
for pressure in start_pressure..=end_pressure { | |
let width = if pressure % 10 == 0 { 0.3 } else if pressure % 5 == 0 { 0.2 } else { 0.1 }; | |
let y_pos = -(pressure - central_pressure) as f64 / (end_pressure - start_pressure) as f64 * range_mm; | |
let line = Line::new() | |
.set("x1", midday_x - day_width / 2. + offset(y_pos, arm_length)) | |
.set("x2", midday_x + day_width / 2. + offset(y_pos, arm_length)) | |
.set("y1", y_pos) | |
.set("y2", y_pos) | |
.set("style", format!("fill: none; stroke: {}; stroke-width: {}", color, width)); | |
document = document.add(line); | |
} | |
// Texts of pressure (separated from the lines, to avoid improper objects overlaps) | |
for pressure in start_pressure..=end_pressure { | |
let y_pos = -(pressure - central_pressure) as f64 / (end_pressure - start_pressure) as f64 * range_mm; | |
if pressure % 10 == 0 { | |
let background = Rectangle::new() | |
.set("x", midday_x + offset(y_pos, arm_length) - 3.) | |
.set("y", y_pos - 1.5) | |
.set("width", 6) | |
.set("height", 3) | |
.set("fill", "white"); | |
let text = Text::new() | |
.set("x", midday_x + offset(y_pos, arm_length)) | |
.set("y", y_pos + 1.) | |
.set("style", format!("font-size:4px;text-align:center;text-anchor:middle;fill:{};font-family:Trattatello", color)) | |
.add(svg::node::Text::new(format!("{}", pressure))); | |
document = document.add(background); | |
document = document.add(text); | |
} | |
} | |
// Day name | |
// It needs a white background to be readable, but a rectangle doesn't look good so I draw | |
// a slanted rectangle (parallelogram) to follow the "midnight" arcs in the header | |
let x_start = midday_x - day_width / 2. + 1.; | |
let data_path_background = Data::new() | |
.move_to((x_start + offset(-header_offset, arm_length), -header_offset)) | |
.line_to((x_start + day_width - 2. + offset(-header_offset, arm_length), -header_offset)) | |
.line_to((x_start + day_width - 2. + offset(-header_offset + 8., arm_length), -header_offset + 8.)) | |
.line_to((x_start + offset(-header_offset + 8., arm_length), -header_offset + 8.)) | |
.close(); | |
let header_background = Path::new() | |
.set("fill", "white") | |
.set("stroke", "none") | |
.set("d", data_path_background); | |
let text = Text::new() | |
.set("x", midday_x + offset(-header_offset + 4.5, arm_length)) | |
.set("y", -header_offset + 4.5) | |
.set("style", format!("font-size:7px;text-align:center;text-anchor:middle;fill:{};font-family:Trattatello", color)) | |
.add(svg::node::Text::new(day.to_uppercase())); | |
document = document.add(header_background); | |
document = document.add(text); | |
// Texts of hours | |
for hour in (0..24).skip(2).step_by(2) { | |
let text = Text::new() | |
.set("x", midday_x + ((hour - 12) as f64) / 24. * day_width + offset(-header_offset + 7.5, arm_length)) // Adding an offset compared to the horizontal line | |
.set("y", -header_offset + 7.5) | |
.set("style", format!("font-size:3px;text-align:center;text-anchor:middle;fill:{};font-family:Trattatello", color)) | |
.add(svg::node::Text::new(format!("{}", hour))); | |
document = document.add(text); | |
} | |
} | |
svg::save("image.svg", &document).unwrap(); | |
} | |
// Computes the x-offset depending on the arm's length and the y position | |
fn offset(y_position: f64, arm_length: f64) -> f64 { | |
let angle = f64::asin(y_position / -arm_length); | |
let offset = arm_length - (arm_length * f64::cos(angle)); | |
offset // No trailing semi-colon to return the value | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment