diff options
| author | Arne Dußin | 2020-11-03 23:41:08 +0100 |
|---|---|---|
| committer | Arne Dußin | 2020-11-03 23:41:08 +0100 |
| commit | f6d06f6123ec6fce5814ee803e0b85052922ad47 (patch) | |
| tree | 5bc6b9ad4de24600efa4f8588d6d9dc2f511c02e /src/svg/style.rs | |
| parent | b5603648a4c88585764ac70db9614504b9b57515 (diff) | |
| download | graf_karto-f6d06f6123ec6fce5814ee803e0b85052922ad47.tar.gz graf_karto-f6d06f6123ec6fce5814ee803e0b85052922ad47.zip | |
Add (very) basic path-drawing
Diffstat (limited to 'src/svg/style.rs')
| -rw-r--r-- | src/svg/style.rs | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/svg/style.rs b/src/svg/style.rs new file mode 100644 index 0000000..78b800d --- /dev/null +++ b/src/svg/style.rs @@ -0,0 +1,172 @@ +use raylib::ffi::Color; +use std::str::FromStr; + +/// Convert an html-style colour into a raylib Color-struct if possible. If there is an error in +/// the formatting, it returns `None`. +pub fn colour_from_html(html: &str) -> Option<Color> { + /* The html-code must be exactly seven characters long, one for the hash and two per primary + * colour. + */ + if html.len() != 7 { + return None; + } + + let extract_hex = |string: &str, pos: usize| { + u8::from_str_radix( + string.get(pos..pos + 2).expect("Could not split string"), + 16, + ) + .ok() + }; + let red: Option<u8> = extract_hex(html, 1); + let green: Option<u8> = extract_hex(html, 3); + let blue: Option<u8> = extract_hex(html, 5); + + if let (Some(r), Some(g), Some(b)) = (red, green, blue) { + Some(Color { r, g, b, a: 255 }) + } else { + None + } +} + +/// The style of the end of the stroke. +/// See [stroke-line-cap property](https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty) +/// in the SVG Documentation. +pub enum StrokeLineCap { + Butt, + Round, + Square, +} + +/// The style of the joining corners of the stroke. +/// See [stroke-line-join property](https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty) +/// in the SVG Documentation +pub enum StrokeLineJoin { + Miter, + Round, + Bevel, +} + +/// The style of a path drawing instruction. +pub struct Style { + pub fill: Option<Color>, + pub stroke: Color, + pub stroke_width: f32, + pub stroke_linecap: StrokeLineCap, + pub stroke_linejoin: StrokeLineJoin, + pub stroke_opacity: f32, +} + +impl FromStr for StrokeLineCap { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "butt" => Ok(Self::Butt), + "round" => Ok(Self::Round), + "square" => Ok(Self::Square), + _ => Err("No such line-cap style".to_owned()), + } + } +} + +impl Default for StrokeLineCap { + fn default() -> Self { + StrokeLineCap::Butt + } +} + +impl FromStr for StrokeLineJoin { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "miter" => Ok(Self::Miter), + "round" => Ok(Self::Round), + "bevel" => Ok(Self::Bevel), + _ => Err("No such line-join style".to_owned()), + } + } +} + +impl Default for StrokeLineJoin { + fn default() -> Self { + StrokeLineJoin::Miter + } +} + +impl FromStr for Style { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + // Split into CSS-attributes + let attributes: Vec<&str> = s.split(';').collect(); + // Split the attributes into name and value pairs and parse them into a style struct + let mut style = Style::default(); + for attribute in attributes { + let attribute_parts: Vec<&str> = attribute.split(':').collect(); + if attribute_parts.len() != 2 { + continue; + } + + match attribute_parts[0].trim() { + "fill" => { + style.fill = match attribute_parts[1].trim() { + "none" => None, + colour => colour_from_html(colour), + } + } + "stroke" => { + style.stroke = match colour_from_html(attribute_parts[1].trim()) { + Some(c) => c, + None => { + return Err(format!( + "Could not parse colour from {}", + attribute_parts[1].trim() + )) + } + } + } + "stroke-width" => { + style.stroke_width = match attribute_parts[1].trim().parse::<f32>() { + Ok(width) => width, + Err(err) => return Err(format!("Could not parse stroke-width: {}", err)), + } + } + "stroke-linecap" => { + style.stroke_linecap = StrokeLineCap::from_str(attribute_parts[1].trim())? + } + "stroke-linejoin" => { + style.stroke_linejoin = StrokeLineJoin::from_str(attribute_parts[1].trim())? + } + "stroke-opacity" => { + style.stroke_width = match attribute_parts[1].trim().parse::<f32>() { + Ok(opacity) => opacity, + Err(err) => return Err(format!("Could not parse stroke-opacity: {}", err)), + } + } + attr => return Err(format!("Unknown attribute {}", attr)), + } + } + + Ok(style) + } +} + +impl Default for Style { + fn default() -> Self { + Self { + fill: None, + stroke: Color { + r: 0, + g: 0, + b: 0, + a: 255, + }, + stroke_width: 1., + stroke_linecap: StrokeLineCap::default(), + stroke_linejoin: StrokeLineJoin::default(), + stroke_opacity: 1., + } + } +} |
