From 3b9c6d58dee043717ddb77a7006c96380bab714d Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 12:20:33 -0700 Subject: [PATCH 1/7] Implement PartialEq for SVGMeasure --- dyesub-tool/src/svg/measure.rs | 13 +++++++++++++ dyesub-tool/src/svg/tests.rs | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/dyesub-tool/src/svg/measure.rs b/dyesub-tool/src/svg/measure.rs index 4a63dd0..7f442aa 100644 --- a/dyesub-tool/src/svg/measure.rs +++ b/dyesub-tool/src/svg/measure.rs @@ -1,4 +1,5 @@ use super::SVGUnit; +use std::cmp::Ordering; use std::fmt; use std::{ fmt::Display, @@ -40,6 +41,18 @@ impl PartialEq for SVGMeasure { } } +impl PartialOrd for SVGMeasure { + fn partial_cmp(&self, other: &Self) -> Option { + Some(if self == other { + Ordering::Equal + } else if self.to_user_units() > other.to_user_units() { + Ordering::Greater + } else { + Ordering::Less + }) + } +} + impl Display for SVGMeasure { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.measure, self.unit) diff --git a/dyesub-tool/src/svg/tests.rs b/dyesub-tool/src/svg/tests.rs index 559e599..fd57bd3 100644 --- a/dyesub-tool/src/svg/tests.rs +++ b/dyesub-tool/src/svg/tests.rs @@ -44,3 +44,11 @@ fn ne() { let b = SVGMeasure::new(10.0, SVGUnit::Pixel); assert_ne!(a, b); } + +#[test] +fn cmp() { + let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter); + let m_5mm = SVGMeasure::new(5.0, SVGUnit::Millimeter); + assert!(m_10cm > m_5mm); + assert!(m_5mm < m_10cm); +} From 5254d3dc8f234c9609fa98f75308a3f449c04456 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 12:29:52 -0700 Subject: [PATCH 2/7] Use strum for SVGUnit --- Cargo.lock | 33 +++++++++++++++++++++++++++++++++ dyesub-tool/Cargo.toml | 2 ++ dyesub-tool/src/svg/unit.rs | 29 +++++++++-------------------- flake.nix | 2 +- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 323df3f..8ae7457 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,8 +77,16 @@ version = "0.1.0" dependencies = [ "askama", "derive_more", + "strum", + "strum_macros", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "humansize" version = "2.1.3" @@ -174,6 +182,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "semver" version = "1.0.18" @@ -200,6 +214,25 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.28", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/dyesub-tool/Cargo.toml b/dyesub-tool/Cargo.toml index 1ff22f0..a67246a 100644 --- a/dyesub-tool/Cargo.toml +++ b/dyesub-tool/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" [dependencies] askama = "0.12.0" derive_more = "0.99.17" +strum = "0.25.0" +strum_macros = "0.25.2" diff --git a/dyesub-tool/src/svg/unit.rs b/dyesub-tool/src/svg/unit.rs index 9c67baf..762a535 100644 --- a/dyesub-tool/src/svg/unit.rs +++ b/dyesub-tool/src/svg/unit.rs @@ -1,19 +1,26 @@ -use std::fmt::{self, Display}; +use strum_macros::{Display, EnumString, IntoStaticStr}; /// https://oreillymedia.github.io/Using_SVG/guide/units.html #[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Display, EnumString, IntoStaticStr)] pub enum SVGUnit { /// Pixel units, directly equivalent to SVG user units. + #[strum(serialize = "px")] Pixel, /// Inches. + #[strum(serialize = "in")] Inch, /// Centimeters. + #[strum(serialize = "cm")] Centimeter, /// Millimeters. + #[strum(serialize = "mm")] Millimeter, /// Points. + #[strum(serialize = "pt")] Point, // Picas. + #[strum(serialize = "pc")] Pica, } @@ -29,22 +36,4 @@ impl SVGUnit { Pica => 16.0, } } -} - -impl Display for SVGUnit { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use SVGUnit::*; - write!( - f, - "{}", - match &self { - Pixel => "px", - Inch => "in", - Centimeter => "cm", - Millimeter => "mm", - Point => "pt", - Pica => "pc", - } - ) - } -} +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index 98edfdf..7042f94 100644 --- a/flake.nix +++ b/flake.nix @@ -51,7 +51,7 @@ Updating `cargoHash`: pname = "dyesub-tool"; version = "0.1.0"; buildAndTestSubdir = "dyesub-tool"; - cargoHash = "sha256-owoTOMY/u7K3Y96FIHXMJeqILAOFR2wSYozzsT+0p64="; + cargoHash = "sha256-WUA3ysG7SlylV1y5nkPxLRGVa4NktFjpawE97d3b36I="; meta = meta // { description = "A tool for generating dye sublimation transfer sheet SVGs for Japanese thumb shift 拇指シフト keycaps."; }; From dc636263b7b315d45c2c2d7f14a94b9eeca25081 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 12:46:19 -0700 Subject: [PATCH 3/7] Implement FromStr for SVGMeasure --- Cargo.lock | 46 ++++++++++++++++++++++++++++++++++ dyesub-tool/Cargo.toml | 2 ++ dyesub-tool/src/svg/measure.rs | 32 +++++++++++++++++++++++ dyesub-tool/src/svg/tests.rs | 12 +++++++++ flake.nix | 2 +- 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8ae7457..3d7b096 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +dependencies = [ + "memchr", +] + [[package]] name = "askama" version = "0.12.0" @@ -77,6 +86,8 @@ version = "0.1.0" dependencies = [ "askama", "derive_more", + "lazy_static", + "regex", "strum", "strum_macros", ] @@ -96,6 +107,12 @@ dependencies = [ "libm", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libm" version = "0.2.7" @@ -173,6 +190,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + [[package]] name = "rustc_version" version = "0.4.0" diff --git a/dyesub-tool/Cargo.toml b/dyesub-tool/Cargo.toml index a67246a..a99af79 100644 --- a/dyesub-tool/Cargo.toml +++ b/dyesub-tool/Cargo.toml @@ -6,5 +6,7 @@ edition = "2021" [dependencies] askama = "0.12.0" derive_more = "0.99.17" +lazy_static = "1.4.0" +regex = "1.9.3" strum = "0.25.0" strum_macros = "0.25.2" diff --git a/dyesub-tool/src/svg/measure.rs b/dyesub-tool/src/svg/measure.rs index 7f442aa..3e23764 100644 --- a/dyesub-tool/src/svg/measure.rs +++ b/dyesub-tool/src/svg/measure.rs @@ -1,10 +1,16 @@ use super::SVGUnit; use std::cmp::Ordering; use std::fmt; +use std::num::ParseFloatError; +use std::str::FromStr; use std::{ fmt::Display, ops::{Add, Div, Mul, Sub}, }; +use lazy_static::lazy_static; +use regex::Regex; +use derive_more::From; +use strum::ParseError; #[derive(Debug, Clone, Copy)] pub struct SVGMeasure { @@ -29,6 +35,32 @@ impl SVGMeasure { } } +#[derive(From, Debug)] +pub enum SVGUnitParseError { + ParseMeasure(ParseFloatError), + ParseUnit(ParseError), + Invalid, +} + +impl FromStr for SVGMeasure { + type Err = SVGUnitParseError; + fn from_str(s: &str) -> Result { + if s == "0" { + return Ok(SVGMeasure::new(0.0, SVGUnit::Pixel)); + } + lazy_static! { + static ref RE: Regex = Regex::new(r"^([\d.]+)([a-zA-Z]+)$").unwrap(); + } + if let Some(captures) = RE.captures(s) { + let measure = captures[1].parse::()?; + let unit = captures[2].parse::()?; + Ok(SVGMeasure::new(measure, unit)) + } else { + Err(SVGUnitParseError::Invalid) + } + } +} + const EQ_TOLERANCE: f64 = 0.00001; impl PartialEq for SVGMeasure { diff --git a/dyesub-tool/src/svg/tests.rs b/dyesub-tool/src/svg/tests.rs index fd57bd3..1c666b9 100644 --- a/dyesub-tool/src/svg/tests.rs +++ b/dyesub-tool/src/svg/tests.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use crate::svg::{SVGMeasure, SVGUnit}; #[test] @@ -52,3 +54,13 @@ fn cmp() { assert!(m_10cm > m_5mm); assert!(m_5mm < m_10cm); } + +#[test] +fn from_str() { + let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter); + let m_5mm = SVGMeasure::new(5.0, SVGUnit::Millimeter); + let m_0 = SVGMeasure::new(0.0, SVGUnit::Pixel); + assert_eq!(m_10cm, SVGMeasure::from_str("10cm").unwrap()); + assert_eq!(m_5mm, SVGMeasure::from_str("5mm").unwrap()); + assert_eq!(m_0, SVGMeasure::from_str("0").unwrap()); +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index 7042f94..7abf022 100644 --- a/flake.nix +++ b/flake.nix @@ -51,7 +51,7 @@ Updating `cargoHash`: pname = "dyesub-tool"; version = "0.1.0"; buildAndTestSubdir = "dyesub-tool"; - cargoHash = "sha256-WUA3ysG7SlylV1y5nkPxLRGVa4NktFjpawE97d3b36I="; + cargoHash = "sha256-QlLyKDZZ+/bB4xBv0j9GoL6Ur3Okdskkazi4WlHhx6Y="; meta = meta // { description = "A tool for generating dye sublimation transfer sheet SVGs for Japanese thumb shift 拇指シフト keycaps."; }; From 3960d6e72141a25d35e7d1728dfb51b024046a65 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 13:58:03 -0700 Subject: [PATCH 4/7] Basic grid output --- dyesub-tool/src/main.rs | 27 ++++++++++++++++++++++++++- dyesub-tool/src/svg/measure.rs | 19 ++++++++++++++++++- dyesub-tool/src/svg/unit.rs | 4 ++-- dyesub-tool/templates/document.xml | 5 ++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/dyesub-tool/src/main.rs b/dyesub-tool/src/main.rs index 0a4c50d..adc69d7 100644 --- a/dyesub-tool/src/main.rs +++ b/dyesub-tool/src/main.rs @@ -8,6 +8,12 @@ use std::{fs::OpenOptions, io::Write}; #[derive(Template)] #[template(path = "document.xml")] struct DocumentTemplate { + dimensions: DocumentDimensions, + box_size: SVGMeasure, + positions: Vec<(SVGMeasure, SVGMeasure)>, +} + +struct DocumentDimensions { width: SVGMeasure, height: SVGMeasure, } @@ -18,16 +24,35 @@ enum Error { Template(askama::Error), } +fn positions(size: SVGMeasure, padding: SVGMeasure, dimensions: &DocumentDimensions, count: u32) -> Vec<(SVGMeasure, SVGMeasure)> { + let grid = size + padding; + let row_count = ((dimensions.width - padding * 2.0) / grid) as u32; + (0..count) + .map(|i| ( + grid * (i % row_count).into() + padding, + grid * (i / row_count).into() + padding, + )) + .collect() +} + fn main() -> Result<(), Error> { let mut file = OpenOptions::new() .write(true) .truncate(true) .create(true) .open("output.svg")?; - let document = DocumentTemplate { + let dimensions = DocumentDimensions { width: SVGMeasure::new(8.5, SVGUnit::Inch), height: SVGMeasure::new(11.0, SVGUnit::Inch), }; + let box_size = SVGMeasure::new(14.0, SVGUnit::Millimeter); + let boxes = 54; + let padding = SVGMeasure::new(5.0, SVGUnit::Millimeter); + let document = DocumentTemplate { + positions: positions(box_size, padding, &dimensions, boxes), + dimensions, + box_size, + }; write!(file, "{}", document.render()?)?; Ok(()) } diff --git a/dyesub-tool/src/svg/measure.rs b/dyesub-tool/src/svg/measure.rs index 3e23764..f9814e6 100644 --- a/dyesub-tool/src/svg/measure.rs +++ b/dyesub-tool/src/svg/measure.rs @@ -5,7 +5,7 @@ use std::num::ParseFloatError; use std::str::FromStr; use std::{ fmt::Display, - ops::{Add, Div, Mul, Sub}, + ops::{Add, Div, Mul, Sub, Rem}, }; use lazy_static::lazy_static; use regex::Regex; @@ -118,6 +118,14 @@ impl Mul for SVGMeasure { } } +impl Div for SVGMeasure { + type Output = f64; + + fn div(self, other: Self) -> Self::Output { + self.measure / other.to_unit(self.unit).measure + } +} + impl Div for SVGMeasure { type Output = Self; @@ -126,3 +134,12 @@ impl Div for SVGMeasure { self } } + +impl Rem for SVGMeasure { + type Output = Self; + + fn rem(mut self, other: Self) -> Self::Output { + self.measure %= other.to_unit(self.unit).measure; + self + } +} diff --git a/dyesub-tool/src/svg/unit.rs b/dyesub-tool/src/svg/unit.rs index 762a535..ddfe527 100644 --- a/dyesub-tool/src/svg/unit.rs +++ b/dyesub-tool/src/svg/unit.rs @@ -25,14 +25,14 @@ pub enum SVGUnit { } impl SVGUnit { - pub fn to_user_units(&self) -> f64 { + pub const fn to_user_units(&self) -> f64 { use SVGUnit::*; match self { Pixel => 1.0, Inch => 96.0, Centimeter => 37.795, Millimeter => 3.7795, - Point => 4.0 / 3.0, // 1.3333 + Point => 1.3333, Pica => 16.0, } } diff --git a/dyesub-tool/templates/document.xml b/dyesub-tool/templates/document.xml index d555166..2f330cd 100644 --- a/dyesub-tool/templates/document.xml +++ b/dyesub-tool/templates/document.xml @@ -1,5 +1,8 @@ + {% for (x, y) in positions %} + + {% endfor %} From 37da862c9f59a909a17cb4ca435c8d7f575b83d3 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 13:58:10 -0700 Subject: [PATCH 5/7] cargo fmt --- dyesub-tool/src/main.rs | 17 ++++++++++++----- dyesub-tool/src/svg/measure.rs | 8 ++++---- dyesub-tool/src/svg/tests.rs | 2 +- dyesub-tool/src/svg/unit.rs | 5 ++--- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/dyesub-tool/src/main.rs b/dyesub-tool/src/main.rs index adc69d7..ad15235 100644 --- a/dyesub-tool/src/main.rs +++ b/dyesub-tool/src/main.rs @@ -24,14 +24,21 @@ enum Error { Template(askama::Error), } -fn positions(size: SVGMeasure, padding: SVGMeasure, dimensions: &DocumentDimensions, count: u32) -> Vec<(SVGMeasure, SVGMeasure)> { +fn positions( + size: SVGMeasure, + padding: SVGMeasure, + dimensions: &DocumentDimensions, + count: u32, +) -> Vec<(SVGMeasure, SVGMeasure)> { let grid = size + padding; let row_count = ((dimensions.width - padding * 2.0) / grid) as u32; (0..count) - .map(|i| ( - grid * (i % row_count).into() + padding, - grid * (i / row_count).into() + padding, - )) + .map(|i| { + ( + grid * (i % row_count).into() + padding, + grid * (i / row_count).into() + padding, + ) + }) .collect() } diff --git a/dyesub-tool/src/svg/measure.rs b/dyesub-tool/src/svg/measure.rs index f9814e6..763e147 100644 --- a/dyesub-tool/src/svg/measure.rs +++ b/dyesub-tool/src/svg/measure.rs @@ -1,15 +1,15 @@ use super::SVGUnit; +use derive_more::From; +use lazy_static::lazy_static; +use regex::Regex; use std::cmp::Ordering; use std::fmt; use std::num::ParseFloatError; use std::str::FromStr; use std::{ fmt::Display, - ops::{Add, Div, Mul, Sub, Rem}, + ops::{Add, Div, Mul, Rem, Sub}, }; -use lazy_static::lazy_static; -use regex::Regex; -use derive_more::From; use strum::ParseError; #[derive(Debug, Clone, Copy)] diff --git a/dyesub-tool/src/svg/tests.rs b/dyesub-tool/src/svg/tests.rs index 1c666b9..c8690b9 100644 --- a/dyesub-tool/src/svg/tests.rs +++ b/dyesub-tool/src/svg/tests.rs @@ -63,4 +63,4 @@ fn from_str() { assert_eq!(m_10cm, SVGMeasure::from_str("10cm").unwrap()); assert_eq!(m_5mm, SVGMeasure::from_str("5mm").unwrap()); assert_eq!(m_0, SVGMeasure::from_str("0").unwrap()); -} \ No newline at end of file +} diff --git a/dyesub-tool/src/svg/unit.rs b/dyesub-tool/src/svg/unit.rs index ddfe527..c653555 100644 --- a/dyesub-tool/src/svg/unit.rs +++ b/dyesub-tool/src/svg/unit.rs @@ -1,8 +1,7 @@ use strum_macros::{Display, EnumString, IntoStaticStr}; /// https://oreillymedia.github.io/Using_SVG/guide/units.html -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[derive(Display, EnumString, IntoStaticStr)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, EnumString, IntoStaticStr)] pub enum SVGUnit { /// Pixel units, directly equivalent to SVG user units. #[strum(serialize = "px")] @@ -36,4 +35,4 @@ impl SVGUnit { Pica => 16.0, } } -} \ No newline at end of file +} From 4b61aef177de4accc4020359adce566376f01e18 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 13:59:47 -0700 Subject: [PATCH 6/7] Fix clippy warnings --- dyesub-tool/src/svg/measure.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/dyesub-tool/src/svg/measure.rs b/dyesub-tool/src/svg/measure.rs index 763e147..fd59c9f 100644 --- a/dyesub-tool/src/svg/measure.rs +++ b/dyesub-tool/src/svg/measure.rs @@ -23,11 +23,11 @@ impl SVGMeasure { Self { measure, unit } } - fn to_user_units(&self) -> f64 { + fn to_user_units(self) -> f64 { self.measure * self.unit.to_user_units() } - fn to_unit(&self, unit: SVGUnit) -> Self { + fn to_unit(self, unit: SVGUnit) -> Self { SVGMeasure { measure: self.to_user_units() / unit.to_user_units(), unit, @@ -67,10 +67,6 @@ impl PartialEq for SVGMeasure { fn eq(&self, other: &Self) -> bool { (self.to_user_units() - other.to_user_units()).abs() < EQ_TOLERANCE } - - fn ne(&self, other: &Self) -> bool { - (self.to_user_units() - other.to_user_units()).abs() > EQ_TOLERANCE - } } impl PartialOrd for SVGMeasure { From 78aae3de62abbb1821dd22812956977949882e86 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 14:10:15 -0700 Subject: [PATCH 7/7] Improve box styling --- dyesub-tool/src/main.rs | 2 +- dyesub-tool/templates/document.xml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dyesub-tool/src/main.rs b/dyesub-tool/src/main.rs index ad15235..ba9def8 100644 --- a/dyesub-tool/src/main.rs +++ b/dyesub-tool/src/main.rs @@ -54,7 +54,7 @@ fn main() -> Result<(), Error> { }; let box_size = SVGMeasure::new(14.0, SVGUnit::Millimeter); let boxes = 54; - let padding = SVGMeasure::new(5.0, SVGUnit::Millimeter); + let padding = SVGMeasure::new(2.5, SVGUnit::Millimeter); let document = DocumentTemplate { positions: positions(box_size, padding, &dimensions, boxes), dimensions, diff --git a/dyesub-tool/templates/document.xml b/dyesub-tool/templates/document.xml index 2f330cd..7804fe5 100644 --- a/dyesub-tool/templates/document.xml +++ b/dyesub-tool/templates/document.xml @@ -1,8 +1,7 @@ - {% for (x, y) in positions %} - + {% endfor %}