Skip to content

Instantly share code, notes, and snippets.

@gsurrel
Created March 10, 2020 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gsurrel/d3b3903260c619814969b9799642a129 to your computer and use it in GitHub Desktop.
Save gsurrel/d3b3903260c619814969b9799642a129 to your computer and use it in GitHub Desktop.
Barometer Sheet Generation
// 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