From 6bef088468a81edcf5015ad33c8a73771273ec16 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Fri, 12 Mar 2021 10:11:15 -0800 Subject: [PATCH 01/22] we can now have 5 args with rgb(r,g,b,a,space) --- src/dreammaker/constants.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index c7a3a7ad..769afd95 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -744,8 +744,8 @@ impl<'a> ConstantFolder<'a> { "arccos" => self.trig_op(args, f32::acos)?, "rgb" => { use std::fmt::Write; - if args.len() != 3 && args.len() != 4 { - return Err(self.error(format!("malformed rgb() call, must have 3 or 4 arguments and instead has {}", args.len()))); + if args.len() != 3 && args.len() != 4 && args.len() != 5 { + return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); } let mut result = String::with_capacity(7); result.push('#'); From a6fb458a4d37dd24722c07d99c4190b25d98d741 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 19:29:39 -0700 Subject: [PATCH 02/22] every day we stray further from the light consistent spacing --- src/dreammaker/builtins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dreammaker/builtins.rs b/src/dreammaker/builtins.rs index 17e28d74..c9301b5c 100644 --- a/src/dreammaker/builtins.rs +++ b/src/dreammaker/builtins.rs @@ -513,7 +513,7 @@ pub fn register_builtins(tree: &mut ObjectTree) { proc/REGEX_QUOTE_REPLACEMENT(text); proc/replacetext(Haystack,Needle,Replacement,Start=1,End=0); proc/replacetextEx(Haystack,Needle,Replacement,Start=1,End=0); - proc/rgb(R,G,B,A=null,space); // special form? - [r,g,b|x,y,z],(a),(space) + proc/rgb(R,G,B,A=null,space,red,blue,green,alpha,h,hue,s,saturation,c,chroma,v,value,l,luminance,y); // [r,g,b|h,s,(v|l|y)],(a),(space) proc/rgb2num(color, space); proc/roll(ndice=1,sides); // +1 form proc/round(A,B=null); From 9ef144eccd49e90f5facd653a584955c98af23a9 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 19:51:41 -0700 Subject: [PATCH 03/22] note on the proper format --- src/dreammaker/builtins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dreammaker/builtins.rs b/src/dreammaker/builtins.rs index c9301b5c..6da3acda 100644 --- a/src/dreammaker/builtins.rs +++ b/src/dreammaker/builtins.rs @@ -513,7 +513,7 @@ pub fn register_builtins(tree: &mut ObjectTree) { proc/REGEX_QUOTE_REPLACEMENT(text); proc/replacetext(Haystack,Needle,Replacement,Start=1,End=0); proc/replacetextEx(Haystack,Needle,Replacement,Start=1,End=0); - proc/rgb(R,G,B,A=null,space,red,blue,green,alpha,h,hue,s,saturation,c,chroma,v,value,l,luminance,y); // [r,g,b|h,s,(v|l|y)],(a),(space) + proc/rgb(R,G,B,A=null,space,red,blue,green,alpha,h,hue,s,saturation,c,chroma,v,value,l,luminance,y); // [r,g,b|h,s,[v|l|y]],(a),(space) proc/rgb2num(color, space); proc/roll(ndice=1,sides); // +1 form proc/round(A,B=null); From 0d70dc66a5d711fa5ccf436a2f032d27567616f3 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 21:37:12 -0700 Subject: [PATCH 04/22] rgb() kwarg validation --- src/dreammaker/constants.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 769afd95..2746a9c5 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -749,12 +749,38 @@ impl<'a> ConstantFolder<'a> { } let mut result = String::with_capacity(7); result.push('#'); - for each in args { - if let Some(i) = self.expr(each, None)?.to_int() { - let clamped = std::cmp::max(::std::cmp::min(i, 255), 0); + + for each in self.arguments(args)? { + let value = each.0.clone(); + let potential_kwarg_value = each.1.clone(); + let mut range = (0, 255); + + let mut to_check = value.clone(); + + // If we have a secondary value, it's a kwarg, we need to get the actual value. If this fails, it's normal. + if let Some(kwarg_value) = potential_kwarg_value { + if let Some(kwarg) = value.as_str() { + to_check = kwarg_value; // Set the value to actually check to be our associated vaue + + range = match kwarg { + "r" | "red" | "g" | "green" | "b" | "blue" => (0, 255), + "h" | "hue" => (0, 360), + "s" | "saturation" => (0, 100), + "v" | "value" => (0, 100), + "l" | "y" | "luminance" => (0, 100), + "a" | "alpha" => (0, 255), + "space" => continue, // Don't range-check the value of the space + _ => return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))), + }; + } else { + return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); + } + } + if let Some(i) = to_check.to_int() { + let clamped = std::cmp::max(::std::cmp::min(i, range.1), range.0); let _ = write!(result, "{:02x}", clamped); } else { - return Err(self.error("malformed rgb() call, argument wasn't an int")); + return Err(self.error("malformed rgb() call, value wasn't an int")); } } Constant::String(result) From 6b7bd06082d0052e2bbe87d7c917571deeb98cf8 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 21:41:08 -0700 Subject: [PATCH 05/22] move position of y arg to clarify it is lumin. --- src/dreammaker/builtins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dreammaker/builtins.rs b/src/dreammaker/builtins.rs index 6da3acda..fed68662 100644 --- a/src/dreammaker/builtins.rs +++ b/src/dreammaker/builtins.rs @@ -513,7 +513,7 @@ pub fn register_builtins(tree: &mut ObjectTree) { proc/REGEX_QUOTE_REPLACEMENT(text); proc/replacetext(Haystack,Needle,Replacement,Start=1,End=0); proc/replacetextEx(Haystack,Needle,Replacement,Start=1,End=0); - proc/rgb(R,G,B,A=null,space,red,blue,green,alpha,h,hue,s,saturation,c,chroma,v,value,l,luminance,y); // [r,g,b|h,s,[v|l|y]],(a),(space) + proc/rgb(R,G,B,A=null,space,red,blue,green,alpha,h,hue,s,saturation,c,chroma,v,value,l,y,luminance); // [r,g,b|h,s,[v|l|y]],(a),(space) proc/rgb2num(color, space); proc/roll(ndice=1,sides); // +1 form proc/round(A,B=null); From 207ecb1aa1c697b2b4ad7d504d012cdd8f3af854 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 21:42:38 -0700 Subject: [PATCH 06/22] unnecessary cloning --- src/dreammaker/constants.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 2746a9c5..8a9dc7e8 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -751,8 +751,8 @@ impl<'a> ConstantFolder<'a> { result.push('#'); for each in self.arguments(args)? { - let value = each.0.clone(); - let potential_kwarg_value = each.1.clone(); + let value = each.0; + let potential_kwarg_value = each.1; let mut range = (0, 255); let mut to_check = value.clone(); From c4ead63820d1542d1f4d9993a6ab59a21c4debd6 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 21:45:57 -0700 Subject: [PATCH 07/22] use Range instead of dumb custom tuple impl --- src/dreammaker/constants.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 8a9dc7e8..ba1425c9 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -753,7 +753,7 @@ impl<'a> ConstantFolder<'a> { for each in self.arguments(args)? { let value = each.0; let potential_kwarg_value = each.1; - let mut range = (0, 255); + let mut range = 0..255; let mut to_check = value.clone(); @@ -763,12 +763,12 @@ impl<'a> ConstantFolder<'a> { to_check = kwarg_value; // Set the value to actually check to be our associated vaue range = match kwarg { - "r" | "red" | "g" | "green" | "b" | "blue" => (0, 255), - "h" | "hue" => (0, 360), - "s" | "saturation" => (0, 100), - "v" | "value" => (0, 100), - "l" | "y" | "luminance" => (0, 100), - "a" | "alpha" => (0, 255), + "r" | "red" | "g" | "green" | "b" | "blue" => 0..255, + "h" | "hue" => 0..360, + "s" | "saturation" => 0..100, + "v" | "value" => 0..100, + "l" | "y" | "luminance" => 0..100, + "a" | "alpha" => 0..255, "space" => continue, // Don't range-check the value of the space _ => return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))), }; @@ -776,8 +776,9 @@ impl<'a> ConstantFolder<'a> { return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); } } + if let Some(i) = to_check.to_int() { - let clamped = std::cmp::max(::std::cmp::min(i, range.1), range.0); + let clamped = std::cmp::max(::std::cmp::min(i, range.end), range.start); let _ = write!(result, "{:02x}", clamped); } else { return Err(self.error("malformed rgb() call, value wasn't an int")); From ee833903e948027ab88f9f9b42ed4a244b82d9e1 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Sun, 14 Mar 2021 22:13:36 -0700 Subject: [PATCH 08/22] now checks if the rgb() value is within the range --- src/dreammaker/constants.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index ba1425c9..f3b778d7 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -6,7 +6,7 @@ use std::path::Path; use linked_hash_map::LinkedHashMap; use ordered_float::OrderedFloat; -use super::{DMError, Location, HasLocation, Context}; +use super::{DMError, Location, HasLocation, Context, Severity}; use super::objtree::*; use super::ast::*; use super::preprocessor::DefineMap; @@ -753,7 +753,7 @@ impl<'a> ConstantFolder<'a> { for each in self.arguments(args)? { let value = each.0; let potential_kwarg_value = each.1; - let mut range = 0..255; + let mut range = 0..=255; let mut to_check = value.clone(); @@ -763,12 +763,12 @@ impl<'a> ConstantFolder<'a> { to_check = kwarg_value; // Set the value to actually check to be our associated vaue range = match kwarg { - "r" | "red" | "g" | "green" | "b" | "blue" => 0..255, - "h" | "hue" => 0..360, - "s" | "saturation" => 0..100, - "v" | "value" => 0..100, - "l" | "y" | "luminance" => 0..100, - "a" | "alpha" => 0..255, + "r" | "red" | "g" | "green" | "b" | "blue" => 0..=255, + "h" | "hue" => 0..=360, + "s" | "saturation" => 0..=100, + "v" | "value" => 0..=100, + "l" | "y" | "luminance" => 0..=100, + "a" | "alpha" => 0..=255, "space" => continue, // Don't range-check the value of the space _ => return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))), }; @@ -778,7 +778,13 @@ impl<'a> ConstantFolder<'a> { } if let Some(i) = to_check.to_int() { - let clamped = std::cmp::max(::std::cmp::min(i, range.end), range.start); + if !range.contains(&i) { + return Err(self.error(format!("malformed rgb() call, {} is not within the valid range ({}..{})", i, range.start(), range.end())) + .set_severity(Severity::Warning) + .with_location(self.location) + ) + } + let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); let _ = write!(result, "{:02x}", clamped); } else { return Err(self.error("malformed rgb() call, value wasn't an int")); From 4c2008c79c0a1e4fd52fc69df8d3774bed96d121 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 10:15:29 -0700 Subject: [PATCH 09/22] some fmt-ing --- src/dreammaker/constants.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index f3b778d7..17318726 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -6,10 +6,10 @@ use std::path::Path; use linked_hash_map::LinkedHashMap; use ordered_float::OrderedFloat; -use super::{DMError, Location, HasLocation, Context, Severity}; -use super::objtree::*; use super::ast::*; +use super::objtree::*; use super::preprocessor::DefineMap; +use super::{Context, DMError, HasLocation, Location, Severity}; /// An absolute typepath and optional variables. /// @@ -478,7 +478,7 @@ fn constant_ident_lookup( .cloned() { Some(decl) => decl, - None => return Ok(ConstLookup::Continue(None)), // definitely doesn't exist + None => return Ok(ConstLookup::Continue(None)), // definitely doesn't exist }; let type_ = &mut tree[ty]; @@ -770,7 +770,9 @@ impl<'a> ConstantFolder<'a> { "l" | "y" | "luminance" => 0..=100, "a" | "alpha" => 0..=255, "space" => continue, // Don't range-check the value of the space - _ => return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))), + _ => { + return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))) + } }; } else { return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); @@ -782,7 +784,7 @@ impl<'a> ConstantFolder<'a> { return Err(self.error(format!("malformed rgb() call, {} is not within the valid range ({}..{})", i, range.start(), range.end())) .set_severity(Severity::Warning) .with_location(self.location) - ) + ); } let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); let _ = write!(result, "{:02x}", clamped); From d0dbdd26126fab50a7c7e3ff671c67f26a9cbe26 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 17:27:44 -0700 Subject: [PATCH 10/22] color conversion, space detection, lots of shit --- Cargo.lock | 9 ++ src/dreammaker/Cargo.toml | 1 + src/dreammaker/constants.rs | 140 +++++++++++++++++++++++- src/dreammaker/tests/constants_tests.rs | 56 +++++++++- 4 files changed, 199 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c68d1e4..f53e0c64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.1" @@ -319,6 +321,12 @@ dependencies = [ "objc", ] +[[package]] +name = "color_space" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3776b2bcc4e914db501bb9be9572dd706e344b9eb8f882894f3daa651d281381" + [[package]] name = "const_fn" version = "0.4.5" @@ -533,6 +541,7 @@ version = "0.1.0" dependencies = [ "bitflags", "builtins-proc-macro", + "color_space", "guard", "interval-tree", "linked-hash-map", diff --git a/src/dreammaker/Cargo.toml b/src/dreammaker/Cargo.toml index 09d28d4e..53dd9a28 100644 --- a/src/dreammaker/Cargo.toml +++ b/src/dreammaker/Cargo.toml @@ -19,6 +19,7 @@ serde_derive = "1.0.103" toml = "0.5.5" guard = "0.5.0" phf = { version = "0.8.0", features = ["macros"] } +color_space = "0.5.3" [dependencies.linked-hash-map] git = "https://github.com/SpaceManiac/linked-hash-map" diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 17318726..bfdf6ee3 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -5,6 +5,7 @@ use std::path::Path; use linked_hash_map::LinkedHashMap; use ordered_float::OrderedFloat; +use color_space::{Rgb, Hsv, Hsl, Lch}; use super::ast::*; use super::objtree::*; @@ -750,12 +751,122 @@ impl<'a> ConstantFolder<'a> { let mut result = String::with_capacity(7); result.push('#'); - for each in self.arguments(args)? { - let value = each.0; - let potential_kwarg_value = each.1; - let mut range = 0..=255; + let mut space = -1; // -1 to indicate not set + let arguments = self.arguments(args)?; + + #[derive(Default)] + struct ColorArgs { + r: bool, + g: bool, + b: bool, + h: bool, + s: bool, + v: bool, + l: bool, + c: bool, + y: bool, + } + + let mut color_args = ColorArgs {..Default::default()}; + + for each in &arguments { + let value = &each.0; + let potential_kwarg_value = &each.1; + + // Check for kwargs if we're in the right form + if let Some(kwarg_value) = potential_kwarg_value { + if let Some(kwarg) = value.as_str() { + match kwarg { + "r" | "red" => color_args.r = true, + "g" | "green" => color_args.g = true, + "b" | "blue" => color_args.b = true, + "h" | "hue" => color_args.h = true, + "s" | "saturation" => color_args.s = true, + "v" | "value" => color_args.v = true, + "l" | "luminance" => color_args.l = true, + "c" | "chroma" => color_args.c = true, + "y" => color_args.y = true, + "a" | "alpha" => continue, // Alpha can be applied to any colorspace + "space" => match kwarg_value.to_int() { // Do we have an actual colorspace specified? Set the values. + Some(0) => space = 0, + Some(1) => space = 1, + Some(2) => space = 2, + Some(3) => space = 3, + _ => { + return Err(self.error(format!("malformed rgb() call, bad color space: {}", kwarg_value))) + } + } + _ => { + return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))) + } + } + } else { + return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); + } + } + } + + // Only set space if it wasn't set manually by the space arg + if space == -1 { + space = 0; // Default + if color_args.r || color_args.g || color_args.b { + // TODO: Add hint here for useless r/g/b kwarg + } + if color_args.h { + if color_args.c && color_args.y { + space = 3; + } + else if color_args.s { + if color_args.v { + space = 1; + } + else if color_args.l { + space = 2; + } + else { + return Err(self.error("malformed rgb() call, could not determine space: only h & s specified")); + } + } + else { + return Err(self.error("malformed rgb() call, could not determine space: only h specified")); + } + } + } - let mut to_check = value.clone(); + let mut value_vec: Vec = vec![]; + + let mut arg_pos = 0; + + for each in &*arguments { + let value = &each.0; + let potential_kwarg_value = &each.1; + let mut to_check = &value.clone(); + + // Determines the range based on predetermined colorspace + let mut range = match arg_pos { + 0 => match space { + 0 => 0..=255, //r + 1 => 0..=360, //h + 2 => 0..=360, //h + 3 => 0..=360, //h + _ => return Err(self.error("malformed rgb() call, illegal color space")), + }, + 1 => match space { + 0 => 0..=255, //g + 1 => 0..=100, //s + 2 => 0..=100, //s + 3 => 0..=100, //c + _ => return Err(self.error("malformed rgb() call, illegal color space")), + }, + 2 => match space { + 0 => 0..=255, //b + 1 => 0..=100, //v + 2 => 0..=100, //l + 3 => 0..=100, //y + _ => return Err(self.error("malformed rgb() call, illegal color space")), + }, + _ => 0..=255, + }; // If we have a secondary value, it's a kwarg, we need to get the actual value. If this fails, it's normal. if let Some(kwarg_value) = potential_kwarg_value { @@ -787,11 +898,28 @@ impl<'a> ConstantFolder<'a> { ); } let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); - let _ = write!(result, "{:02x}", clamped); + value_vec.push(clamped.into()); } else { return Err(self.error("malformed rgb() call, value wasn't an int")); } + + arg_pos += 1; } + + assert_eq!(value_vec.len(), 3); // Make sure we got 3 values + + // Convert our color given a space to a rgb hexcode + let color: Rgb = match space { + 0 => Rgb::new(value_vec[0], value_vec[1], value_vec[2]), + 1 => Hsv::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), + 2 => Hsl::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), + 3 => Lch::new(value_vec[2], value_vec[1], value_vec[0]).into(), + _ => { + return Err(self.error(format!("malformed rgb() call, bad color space: {}", space))) + } + }; + + let _ = write!(result, "{:02x}{:02x}{:02x}", color.r as u8, color.g as u8, color.b as u8); Constant::String(result) }, "defined" if self.defines.is_some() => { diff --git a/src/dreammaker/tests/constants_tests.rs b/src/dreammaker/tests/constants_tests.rs index 88be9789..140d7984 100644 --- a/src/dreammaker/tests/constants_tests.rs +++ b/src/dreammaker/tests/constants_tests.rs @@ -1,6 +1,6 @@ extern crate dreammaker as dm; -use dm::constants::*; +use dm::{constants::*}; #[test] fn floating_point_rgb() { @@ -13,3 +13,57 @@ fn floating_point_rgb() { Constant::String("#7f7f7f".to_owned()), ); } + +#[test] +fn rgb_base() { + let code_good = "rgb(0, 255, 0)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_good.as_bytes()) + .expect("evaluation failed"), + Constant::String("#00ff00".to_owned()), + ); + +} + +#[test] +fn rgb_range() { + let code_rgb = "rgb(0, 300, 0)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_rgb.as_bytes()) + .unwrap_err().description(), + "malformed rgb() call, 300 is not within the valid range (0..255)", + ); +} + +#[test] +fn rgb_args() { + let code = "rgb(50)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code.as_bytes()) + .unwrap_err().description(), + "malformed rgb() call, must have 3, 4, or 5 arguments and instead has 1", + ); +} + +#[test] +fn rgb_hsl() { + let code_hsl = "rgb(h=0, s=0, l=100)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsl.as_bytes()) + .expect("evaluation failed"), + Constant::String("#ffffff".to_owned()), + ); + // let code_hsl2 = "rgb(h=50, s=60, l=70)"; + // assert_eq!( + // dm::constants::evaluate_str(Default::default(), code_hsl2.as_bytes()) + // .expect("evaluation failed"), + // Constant::String("#e0d185".to_owned()), + // ); + + let code_hsl_space = "rgb(360, 0, 0, space=2)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsl_space.as_bytes()) + .expect("evaluation failed"), + Constant::String("#000000".to_owned()), + ); +} From 0accb0df6a826a66f2805f6f42285cb18f36d740 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 17:59:52 -0700 Subject: [PATCH 11/22] Should be all finished, tests all done except for alpha handling --- src/dreammaker/constants.rs | 9 ++-- src/dreammaker/tests/constants_tests.rs | 72 ++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index bfdf6ee3..927ea046 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -5,7 +5,7 @@ use std::path::Path; use linked_hash_map::LinkedHashMap; use ordered_float::OrderedFloat; -use color_space::{Rgb, Hsv, Hsl, Lch}; +use color_space::{Hsl, Hsv, Lch, Rgb}; use super::ast::*; use super::objtree::*; @@ -878,6 +878,7 @@ impl<'a> ConstantFolder<'a> { "h" | "hue" => 0..=360, "s" | "saturation" => 0..=100, "v" | "value" => 0..=100, + "c" | "chroma" => 0..=100, "l" | "y" | "luminance" => 0..=100, "a" | "alpha" => 0..=255, "space" => continue, // Don't range-check the value of the space @@ -897,7 +898,7 @@ impl<'a> ConstantFolder<'a> { .with_location(self.location) ); } - let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); + let clamped= std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); value_vec.push(clamped.into()); } else { return Err(self.error("malformed rgb() call, value wasn't an int")); @@ -906,7 +907,7 @@ impl<'a> ConstantFolder<'a> { arg_pos += 1; } - assert_eq!(value_vec.len(), 3); // Make sure we got 3 values + assert!(value_vec.len() >= 3); // Make sure we got 3+ values // Convert our color given a space to a rgb hexcode let color: Rgb = match space { @@ -919,7 +920,7 @@ impl<'a> ConstantFolder<'a> { } }; - let _ = write!(result, "{:02x}{:02x}{:02x}", color.r as u8, color.g as u8, color.b as u8); + let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); // APPARENTLY the author thinks fractional rgb is a thing Constant::String(result) }, "defined" if self.defines.is_some() => { diff --git a/src/dreammaker/tests/constants_tests.rs b/src/dreammaker/tests/constants_tests.rs index 140d7984..b5f4b219 100644 --- a/src/dreammaker/tests/constants_tests.rs +++ b/src/dreammaker/tests/constants_tests.rs @@ -22,7 +22,12 @@ fn rgb_base() { .expect("evaluation failed"), Constant::String("#00ff00".to_owned()), ); - + let code_good2 = "rgb(50, 50, 50)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_good2.as_bytes()) + .expect("evaluation failed"), + Constant::String("#323232".to_owned()), + ); } #[test] @@ -33,6 +38,12 @@ fn rgb_range() { .unwrap_err().description(), "malformed rgb() call, 300 is not within the valid range (0..255)", ); + let code_hsv = "rgb(361, 0, 0, space=1)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsv.as_bytes()) + .unwrap_err().description(), + "malformed rgb() call, 361 is not within the valid range (0..360)", + ); } #[test] @@ -45,6 +56,29 @@ fn rgb_args() { ); } +#[test] +fn rgb_hsv() { + let code_hsv = "rgb(h=0, s=0, v=100)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsv.as_bytes()) + .expect("evaluation failed"), + Constant::String("#ffffff".to_owned()), + ); + let code_hsv2 = "rgb(h=50, s=50, v=50)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsv2.as_bytes()) + .expect("evaluation failed"), + Constant::String("#807540".to_owned()), + ); + + let code_hsv_space = "rgb(360, 0, 0, space=1)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsv_space.as_bytes()) + .expect("evaluation failed"), + Constant::String("#000000".to_owned()), + ); +} + #[test] fn rgb_hsl() { let code_hsl = "rgb(h=0, s=0, l=100)"; @@ -53,12 +87,12 @@ fn rgb_hsl() { .expect("evaluation failed"), Constant::String("#ffffff".to_owned()), ); - // let code_hsl2 = "rgb(h=50, s=60, l=70)"; - // assert_eq!( - // dm::constants::evaluate_str(Default::default(), code_hsl2.as_bytes()) - // .expect("evaluation failed"), - // Constant::String("#e0d185".to_owned()), - // ); + let code_hsl2 = "rgb(h=50, s=50, l=50)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsl2.as_bytes()) + .expect("evaluation failed"), + Constant::String("#bfaa40".to_owned()), + ); let code_hsl_space = "rgb(360, 0, 0, space=2)"; assert_eq!( @@ -67,3 +101,27 @@ fn rgb_hsl() { Constant::String("#000000".to_owned()), ); } + + +#[test] +fn rgb_hcy() { + let code_hsl = "rgb(h=0, c=0, y=100)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsl.as_bytes()) + .expect("evaluation failed"), + Constant::String("#ffffff".to_owned()), + ); + let code_hsl2 = "rgb(h=50, c=50, y=50)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsl2.as_bytes()) + .expect("evaluation failed"), + Constant::String("#b65f37".to_owned()), + ); + + let code_hsl_space = "rgb(360, 0, 0, space=3)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsl_space.as_bytes()) + .expect("evaluation failed"), + Constant::String("#000000".to_owned()), + ); +} From 3994041d1dbcde92ebf18698f1d20d9a11059861 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 18:08:00 -0700 Subject: [PATCH 12/22] working alpha for kwarg --- src/dreammaker/constants.rs | 11 +++++++++-- src/dreammaker/tests/constants_tests.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 927ea046..f37077c2 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -765,6 +765,7 @@ impl<'a> ConstantFolder<'a> { l: bool, c: bool, y: bool, + a: Option, } let mut color_args = ColorArgs {..Default::default()}; @@ -786,7 +787,7 @@ impl<'a> ConstantFolder<'a> { "l" | "luminance" => color_args.l = true, "c" | "chroma" => color_args.c = true, "y" => color_args.y = true, - "a" | "alpha" => continue, // Alpha can be applied to any colorspace + "a" | "alpha" => color_args.a = kwarg_value.to_int(), "space" => match kwarg_value.to_int() { // Do we have an actual colorspace specified? Set the values. Some(0) => space = 0, Some(1) => space = 1, @@ -920,7 +921,13 @@ impl<'a> ConstantFolder<'a> { } }; - let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); // APPARENTLY the author thinks fractional rgb is a thing + // APPARENTLY the author thinks fractional rgb is a thing, hence the rounding + if let Some(alpha) = color_args.a { + let _ = write!(result, "{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha); + } else { + let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); + } + Constant::String(result) }, "defined" if self.defines.is_some() => { diff --git a/src/dreammaker/tests/constants_tests.rs b/src/dreammaker/tests/constants_tests.rs index b5f4b219..d2e08d82 100644 --- a/src/dreammaker/tests/constants_tests.rs +++ b/src/dreammaker/tests/constants_tests.rs @@ -56,6 +56,22 @@ fn rgb_args() { ); } +#[test] +fn rgb_alpha() { + let code_hsv = "rgb(h=0, s=0, v=100, a=50, space=1)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsv.as_bytes()) + .expect("evaluation failed"), + Constant::String("#ffffff32".to_owned()), + ); + let code_hsv2 = "rgb(h=0, s=0, v=100, 50)"; + assert_eq!( + dm::constants::evaluate_str(Default::default(), code_hsv2.as_bytes()) + .expect("evaluation failed"), + Constant::String("#ffffff32".to_owned()), + ); +} + #[test] fn rgb_hsv() { let code_hsv = "rgb(h=0, s=0, v=100)"; From 18d13a7ab117a54a6b4317cb57a7eac723fabdd1 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 18:19:02 -0700 Subject: [PATCH 13/22] ...and now positional alpha support --- src/dreammaker/constants.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index f37077c2..2f6629e4 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -921,9 +921,18 @@ impl<'a> ConstantFolder<'a> { } }; + let alpha; + + // Extract the raw 4th alpha positional argument if it wasn't a kwarg + if color_args.a.is_none() && value_vec.len() > 3 { + alpha = Some(value_vec[3] as i32); + } else { // Handle the kwarg if it was there + alpha = color_args.a; + } + // APPARENTLY the author thinks fractional rgb is a thing, hence the rounding - if let Some(alpha) = color_args.a { - let _ = write!(result, "{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha); + if alpha.is_some() { + let _ = write!(result, "{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha.unwrap()); } else { let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); } From f19e6d77abfe2ba1a3211e671a8d430b1eb3be1d Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 23:28:28 -0700 Subject: [PATCH 14/22] Update src/dreammaker/tests/constants_tests.rs --- src/dreammaker/tests/constants_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dreammaker/tests/constants_tests.rs b/src/dreammaker/tests/constants_tests.rs index d2e08d82..dd304ea2 100644 --- a/src/dreammaker/tests/constants_tests.rs +++ b/src/dreammaker/tests/constants_tests.rs @@ -1,6 +1,6 @@ extern crate dreammaker as dm; -use dm::{constants::*}; +use dm::constants::*; #[test] fn floating_point_rgb() { From e7b793e0049ff736a435169a27746a7a0e6999b7 Mon Sep 17 00:00:00 2001 From: ZeWaka Date: Mon, 15 Mar 2021 23:34:11 -0700 Subject: [PATCH 15/22] Update src/dreammaker/constants.rs --- src/dreammaker/constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 2f6629e4..cf8d395e 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -899,7 +899,7 @@ impl<'a> ConstantFolder<'a> { .with_location(self.location) ); } - let clamped= std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); + let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); value_vec.push(clamped.into()); } else { return Err(self.error("malformed rgb() call, value wasn't an int")); From 70cc95effeac978e951db158b5b72cbae1e4c7cb Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:10:36 -0700 Subject: [PATCH 16/22] Move rgb() body to its own function --- src/dreammaker/constants.rs | 394 ++++++++++++++++++------------------ 1 file changed, 198 insertions(+), 196 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index cf8d395e..14c77e38 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -743,202 +743,7 @@ impl<'a> ConstantFolder<'a> { "cos" => self.trig_op(args, f32::cos)?, "arcsin" => self.trig_op(args, f32::asin)?, "arccos" => self.trig_op(args, f32::acos)?, - "rgb" => { - use std::fmt::Write; - if args.len() != 3 && args.len() != 4 && args.len() != 5 { - return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); - } - let mut result = String::with_capacity(7); - result.push('#'); - - let mut space = -1; // -1 to indicate not set - let arguments = self.arguments(args)?; - - #[derive(Default)] - struct ColorArgs { - r: bool, - g: bool, - b: bool, - h: bool, - s: bool, - v: bool, - l: bool, - c: bool, - y: bool, - a: Option, - } - - let mut color_args = ColorArgs {..Default::default()}; - - for each in &arguments { - let value = &each.0; - let potential_kwarg_value = &each.1; - - // Check for kwargs if we're in the right form - if let Some(kwarg_value) = potential_kwarg_value { - if let Some(kwarg) = value.as_str() { - match kwarg { - "r" | "red" => color_args.r = true, - "g" | "green" => color_args.g = true, - "b" | "blue" => color_args.b = true, - "h" | "hue" => color_args.h = true, - "s" | "saturation" => color_args.s = true, - "v" | "value" => color_args.v = true, - "l" | "luminance" => color_args.l = true, - "c" | "chroma" => color_args.c = true, - "y" => color_args.y = true, - "a" | "alpha" => color_args.a = kwarg_value.to_int(), - "space" => match kwarg_value.to_int() { // Do we have an actual colorspace specified? Set the values. - Some(0) => space = 0, - Some(1) => space = 1, - Some(2) => space = 2, - Some(3) => space = 3, - _ => { - return Err(self.error(format!("malformed rgb() call, bad color space: {}", kwarg_value))) - } - } - _ => { - return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))) - } - } - } else { - return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); - } - } - } - - // Only set space if it wasn't set manually by the space arg - if space == -1 { - space = 0; // Default - if color_args.r || color_args.g || color_args.b { - // TODO: Add hint here for useless r/g/b kwarg - } - if color_args.h { - if color_args.c && color_args.y { - space = 3; - } - else if color_args.s { - if color_args.v { - space = 1; - } - else if color_args.l { - space = 2; - } - else { - return Err(self.error("malformed rgb() call, could not determine space: only h & s specified")); - } - } - else { - return Err(self.error("malformed rgb() call, could not determine space: only h specified")); - } - } - } - - let mut value_vec: Vec = vec![]; - - let mut arg_pos = 0; - - for each in &*arguments { - let value = &each.0; - let potential_kwarg_value = &each.1; - let mut to_check = &value.clone(); - - // Determines the range based on predetermined colorspace - let mut range = match arg_pos { - 0 => match space { - 0 => 0..=255, //r - 1 => 0..=360, //h - 2 => 0..=360, //h - 3 => 0..=360, //h - _ => return Err(self.error("malformed rgb() call, illegal color space")), - }, - 1 => match space { - 0 => 0..=255, //g - 1 => 0..=100, //s - 2 => 0..=100, //s - 3 => 0..=100, //c - _ => return Err(self.error("malformed rgb() call, illegal color space")), - }, - 2 => match space { - 0 => 0..=255, //b - 1 => 0..=100, //v - 2 => 0..=100, //l - 3 => 0..=100, //y - _ => return Err(self.error("malformed rgb() call, illegal color space")), - }, - _ => 0..=255, - }; - - // If we have a secondary value, it's a kwarg, we need to get the actual value. If this fails, it's normal. - if let Some(kwarg_value) = potential_kwarg_value { - if let Some(kwarg) = value.as_str() { - to_check = kwarg_value; // Set the value to actually check to be our associated vaue - - range = match kwarg { - "r" | "red" | "g" | "green" | "b" | "blue" => 0..=255, - "h" | "hue" => 0..=360, - "s" | "saturation" => 0..=100, - "v" | "value" => 0..=100, - "c" | "chroma" => 0..=100, - "l" | "y" | "luminance" => 0..=100, - "a" | "alpha" => 0..=255, - "space" => continue, // Don't range-check the value of the space - _ => { - return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))) - } - }; - } else { - return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); - } - } - - if let Some(i) = to_check.to_int() { - if !range.contains(&i) { - return Err(self.error(format!("malformed rgb() call, {} is not within the valid range ({}..{})", i, range.start(), range.end())) - .set_severity(Severity::Warning) - .with_location(self.location) - ); - } - let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); - value_vec.push(clamped.into()); - } else { - return Err(self.error("malformed rgb() call, value wasn't an int")); - } - - arg_pos += 1; - } - - assert!(value_vec.len() >= 3); // Make sure we got 3+ values - - // Convert our color given a space to a rgb hexcode - let color: Rgb = match space { - 0 => Rgb::new(value_vec[0], value_vec[1], value_vec[2]), - 1 => Hsv::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), - 2 => Hsl::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), - 3 => Lch::new(value_vec[2], value_vec[1], value_vec[0]).into(), - _ => { - return Err(self.error(format!("malformed rgb() call, bad color space: {}", space))) - } - }; - - let alpha; - - // Extract the raw 4th alpha positional argument if it wasn't a kwarg - if color_args.a.is_none() && value_vec.len() > 3 { - alpha = Some(value_vec[3] as i32); - } else { // Handle the kwarg if it was there - alpha = color_args.a; - } - - // APPARENTLY the author thinks fractional rgb is a thing, hence the rounding - if alpha.is_some() { - let _ = write!(result, "{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha.unwrap()); - } else { - let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); - } - - Constant::String(result) - }, + "rgb" => Constant::String(self.rgb(args)?), "defined" if self.defines.is_some() => { let defines = self.defines.unwrap(); // annoying, but keeps the match clean if args.len() != 1 { @@ -1036,4 +841,201 @@ impl<'a> ConstantFolder<'a> { } Err(self.error(format!("unknown variable: {}", ident))) } + + fn rgb(&mut self, args: Vec) -> Result { + use std::fmt::Write; + if args.len() != 3 && args.len() != 4 && args.len() != 5 { + return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); + } + let mut result = String::with_capacity(7); + result.push('#'); + + let mut space = -1; // -1 to indicate not set + let arguments = self.arguments(args)?; + + #[derive(Default)] + struct ColorArgs { + r: bool, + g: bool, + b: bool, + h: bool, + s: bool, + v: bool, + l: bool, + c: bool, + y: bool, + a: Option, + } + + let mut color_args = ColorArgs {..Default::default()}; + + for each in &arguments { + let value = &each.0; + let potential_kwarg_value = &each.1; + + // Check for kwargs if we're in the right form + if let Some(kwarg_value) = potential_kwarg_value { + if let Some(kwarg) = value.as_str() { + match kwarg { + "r" | "red" => color_args.r = true, + "g" | "green" => color_args.g = true, + "b" | "blue" => color_args.b = true, + "h" | "hue" => color_args.h = true, + "s" | "saturation" => color_args.s = true, + "v" | "value" => color_args.v = true, + "l" | "luminance" => color_args.l = true, + "c" | "chroma" => color_args.c = true, + "y" => color_args.y = true, + "a" | "alpha" => color_args.a = kwarg_value.to_int(), + "space" => match kwarg_value.to_int() { // Do we have an actual colorspace specified? Set the values. + Some(0) => space = 0, + Some(1) => space = 1, + Some(2) => space = 2, + Some(3) => space = 3, + _ => { + return Err(self.error(format!("malformed rgb() call, bad color space: {}", kwarg_value))) + } + } + _ => { + return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))) + } + } + } else { + return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); + } + } + } + + // Only set space if it wasn't set manually by the space arg + if space == -1 { + space = 0; // Default + if color_args.r || color_args.g || color_args.b { + // TODO: Add hint here for useless r/g/b kwarg + } + if color_args.h { + if color_args.c && color_args.y { + space = 3; + } + else if color_args.s { + if color_args.v { + space = 1; + } + else if color_args.l { + space = 2; + } + else { + return Err(self.error("malformed rgb() call, could not determine space: only h & s specified")); + } + } + else { + return Err(self.error("malformed rgb() call, could not determine space: only h specified")); + } + } + } + + let mut value_vec: Vec = vec![]; + + let mut arg_pos = 0; + + for each in &*arguments { + let value = &each.0; + let potential_kwarg_value = &each.1; + let mut to_check = &value.clone(); + + // Determines the range based on predetermined colorspace + let mut range = match arg_pos { + 0 => match space { + 0 => 0..=255, //r + 1 => 0..=360, //h + 2 => 0..=360, //h + 3 => 0..=360, //h + _ => return Err(self.error("malformed rgb() call, illegal color space")), + }, + 1 => match space { + 0 => 0..=255, //g + 1 => 0..=100, //s + 2 => 0..=100, //s + 3 => 0..=100, //c + _ => return Err(self.error("malformed rgb() call, illegal color space")), + }, + 2 => match space { + 0 => 0..=255, //b + 1 => 0..=100, //v + 2 => 0..=100, //l + 3 => 0..=100, //y + _ => return Err(self.error("malformed rgb() call, illegal color space")), + }, + _ => 0..=255, + }; + + // If we have a secondary value, it's a kwarg, we need to get the actual value. If this fails, it's normal. + if let Some(kwarg_value) = potential_kwarg_value { + if let Some(kwarg) = value.as_str() { + to_check = kwarg_value; // Set the value to actually check to be our associated vaue + + range = match kwarg { + "r" | "red" | "g" | "green" | "b" | "blue" => 0..=255, + "h" | "hue" => 0..=360, + "s" | "saturation" => 0..=100, + "v" | "value" => 0..=100, + "c" | "chroma" => 0..=100, + "l" | "y" | "luminance" => 0..=100, + "a" | "alpha" => 0..=255, + "space" => continue, // Don't range-check the value of the space + _ => { + return Err(self.error(format!("malformed rgb() call, bad kwarg passed: {}", kwarg))) + } + }; + } else { + return Err(self.error(format!("malformed rgb() call, kwarg is not string: {}", value))); + } + } + + if let Some(i) = to_check.to_int() { + if !range.contains(&i) { + return Err(self.error(format!("malformed rgb() call, {} is not within the valid range ({}..{})", i, range.start(), range.end())) + .set_severity(Severity::Warning) + .with_location(self.location) + ); + } + let clamped = std::cmp::max(::std::cmp::min(i, *range.end()), *range.start()); + value_vec.push(clamped.into()); + } else { + return Err(self.error("malformed rgb() call, value wasn't an int")); + } + + arg_pos += 1; + } + + assert!(value_vec.len() >= 3); // Make sure we got 3+ values + + // Convert our color given a space to a rgb hexcode + let color: Rgb = match space { + 0 => Rgb::new(value_vec[0], value_vec[1], value_vec[2]), + 1 => Hsv::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), + 2 => Hsl::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), + 3 => Lch::new(value_vec[2], value_vec[1], value_vec[0]).into(), + _ => { + return Err(self.error(format!("malformed rgb() call, bad color space: {}", space))) + } + }; + + let alpha; + + // Extract the raw 4th alpha positional argument if it wasn't a kwarg + if color_args.a.is_none() && value_vec.len() > 3 { + alpha = Some(value_vec[3] as i32); + } else { // Handle the kwarg if it was there + alpha = color_args.a; + } + + // APPARENTLY the author thinks fractional rgb is a thing, hence the rounding + if alpha.is_some() { + let _ = write!(result, "{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha.unwrap()); + } else { + let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); + } + + Ok(result) + } } From 081453a227ba10fba8fff7150bedc670268ead4a Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:13:22 -0700 Subject: [PATCH 17/22] Replace write! with format! --- src/dreammaker/constants.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 14c77e38..cb4abd84 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -843,12 +843,9 @@ impl<'a> ConstantFolder<'a> { } fn rgb(&mut self, args: Vec) -> Result { - use std::fmt::Write; if args.len() != 3 && args.len() != 4 && args.len() != 5 { return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); } - let mut result = String::with_capacity(7); - result.push('#'); let mut space = -1; // -1 to indicate not set let arguments = self.arguments(args)?; @@ -1030,12 +1027,10 @@ impl<'a> ConstantFolder<'a> { } // APPARENTLY the author thinks fractional rgb is a thing, hence the rounding - if alpha.is_some() { - let _ = write!(result, "{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha.unwrap()); + if let Some(alpha) = alpha { + Ok(format!("#{:02x}{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8, alpha)) } else { - let _ = write!(result, "{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8); + Ok(format!("#{:02x}{:02x}{:02x}", color.r.round() as u8, color.g.round() as u8, color.b.round() as u8)) } - - Ok(result) } } From 827fdf7c5476ddc100bc729ec8749cb5d3979161 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:18:59 -0700 Subject: [PATCH 18/22] Use an enum for the color space --- src/dreammaker/constants.rs | 88 ++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index cb4abd84..1cdebb0e 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -847,7 +847,14 @@ impl<'a> ConstantFolder<'a> { return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); } - let mut space = -1; // -1 to indicate not set + enum ColorSpace { + Rgb = 0, + Hsv = 1, + Hsl = 2, + Hcy = 3, + } + + let mut space = None; let arguments = self.arguments(args)?; #[derive(Default)] @@ -864,7 +871,7 @@ impl<'a> ConstantFolder<'a> { a: Option, } - let mut color_args = ColorArgs {..Default::default()}; + let mut color_args = ColorArgs::default(); for each in &arguments { let value = &each.0; @@ -885,10 +892,10 @@ impl<'a> ConstantFolder<'a> { "y" => color_args.y = true, "a" | "alpha" => color_args.a = kwarg_value.to_int(), "space" => match kwarg_value.to_int() { // Do we have an actual colorspace specified? Set the values. - Some(0) => space = 0, - Some(1) => space = 1, - Some(2) => space = 2, - Some(3) => space = 3, + Some(0) => space = Some(ColorSpace::Rgb), + Some(1) => space = Some(ColorSpace::Hsv), + Some(2) => space = Some(ColorSpace::Hsl), + Some(3) => space = Some(ColorSpace::Hcy), _ => { return Err(self.error(format!("malformed rgb() call, bad color space: {}", kwarg_value))) } @@ -904,31 +911,30 @@ impl<'a> ConstantFolder<'a> { } // Only set space if it wasn't set manually by the space arg - if space == -1 { - space = 0; // Default + let space = if let Some(space) = space { + space + } else { if color_args.r || color_args.g || color_args.b { // TODO: Add hint here for useless r/g/b kwarg - } - if color_args.h { + ColorSpace::Rgb + } else if color_args.h { if color_args.c && color_args.y { - space = 3; - } - else if color_args.s { + ColorSpace::Hcy + } else if color_args.s { if color_args.v { - space = 1; - } - else if color_args.l { - space = 2; - } - else { + ColorSpace::Hsv + } else if color_args.l { + ColorSpace::Hsl + } else { return Err(self.error("malformed rgb() call, could not determine space: only h & s specified")); } - } - else { + } else { return Err(self.error("malformed rgb() call, could not determine space: only h specified")); } + } else { + ColorSpace::Rgb // Default } - } + }; let mut value_vec: Vec = vec![]; @@ -942,25 +948,22 @@ impl<'a> ConstantFolder<'a> { // Determines the range based on predetermined colorspace let mut range = match arg_pos { 0 => match space { - 0 => 0..=255, //r - 1 => 0..=360, //h - 2 => 0..=360, //h - 3 => 0..=360, //h - _ => return Err(self.error("malformed rgb() call, illegal color space")), + ColorSpace::Rgb => 0..=255, //r + ColorSpace::Hsv => 0..=360, //h + ColorSpace::Hsl => 0..=360, //h + ColorSpace::Hcy => 0..=360, //h }, 1 => match space { - 0 => 0..=255, //g - 1 => 0..=100, //s - 2 => 0..=100, //s - 3 => 0..=100, //c - _ => return Err(self.error("malformed rgb() call, illegal color space")), + ColorSpace::Rgb => 0..=255, //g + ColorSpace::Hsv => 0..=100, //s + ColorSpace::Hsl => 0..=100, //s + ColorSpace::Hcy => 0..=100, //c }, 2 => match space { - 0 => 0..=255, //b - 1 => 0..=100, //v - 2 => 0..=100, //l - 3 => 0..=100, //y - _ => return Err(self.error("malformed rgb() call, illegal color space")), + ColorSpace::Rgb => 0..=255, //b + ColorSpace::Hsv => 0..=100, //v + ColorSpace::Hsl => 0..=100, //l + ColorSpace::Hcy => 0..=100, //y }, _ => 0..=255, }; @@ -1008,13 +1011,10 @@ impl<'a> ConstantFolder<'a> { // Convert our color given a space to a rgb hexcode let color: Rgb = match space { - 0 => Rgb::new(value_vec[0], value_vec[1], value_vec[2]), - 1 => Hsv::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), - 2 => Hsl::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), - 3 => Lch::new(value_vec[2], value_vec[1], value_vec[0]).into(), - _ => { - return Err(self.error(format!("malformed rgb() call, bad color space: {}", space))) - } + ColorSpace::Rgb => Rgb::new(value_vec[0], value_vec[1], value_vec[2]), + ColorSpace::Hsv => Hsv::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), + ColorSpace::Hsl => Hsl::new(value_vec[0], value_vec[1] * 0.01, value_vec[2] * 0.01).into(), + ColorSpace::Hcy => Lch::new(value_vec[2], value_vec[1], value_vec[0]).into(), }; let alpha; From 5f1c9bb6ac6dd689d0a54c69bca6ab2fac053842 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:24:53 -0700 Subject: [PATCH 19/22] Use enumerate() for arg_pos --- src/dreammaker/constants.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 1cdebb0e..73fec507 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -938,9 +938,7 @@ impl<'a> ConstantFolder<'a> { let mut value_vec: Vec = vec![]; - let mut arg_pos = 0; - - for each in &*arguments { + for (arg_pos, each) in arguments.iter().enumerate() { let value = &each.0; let potential_kwarg_value = &each.1; let mut to_check = &value.clone(); @@ -1003,8 +1001,6 @@ impl<'a> ConstantFolder<'a> { } else { return Err(self.error("malformed rgb() call, value wasn't an int")); } - - arg_pos += 1; } assert!(value_vec.len() >= 3); // Make sure we got 3+ values From f185feba80a1b145086efa1c9c0df219a63cbd43 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:25:07 -0700 Subject: [PATCH 20/22] Use .get() rather than .len() and [3] --- src/dreammaker/constants.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 73fec507..c92669f2 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -1013,14 +1013,8 @@ impl<'a> ConstantFolder<'a> { ColorSpace::Hcy => Lch::new(value_vec[2], value_vec[1], value_vec[0]).into(), }; - let alpha; - // Extract the raw 4th alpha positional argument if it wasn't a kwarg - if color_args.a.is_none() && value_vec.len() > 3 { - alpha = Some(value_vec[3] as i32); - } else { // Handle the kwarg if it was there - alpha = color_args.a; - } + let alpha = color_args.a.or(value_vec.get(3).map(|&x| x as i32)); // APPARENTLY the author thinks fractional rgb is a thing, hence the rounding if let Some(alpha) = alpha { From cdf0a06782aa1d06dd17cd9ab512dbc8cc09fa8f Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:28:42 -0700 Subject: [PATCH 21/22] Destructure in for loop and skip unnecessary clone --- src/dreammaker/constants.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index c92669f2..6e7db97b 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -873,10 +873,7 @@ impl<'a> ConstantFolder<'a> { let mut color_args = ColorArgs::default(); - for each in &arguments { - let value = &each.0; - let potential_kwarg_value = &each.1; - + for (value, potential_kwarg_value) in &arguments { // Check for kwargs if we're in the right form if let Some(kwarg_value) = potential_kwarg_value { if let Some(kwarg) = value.as_str() { @@ -938,10 +935,8 @@ impl<'a> ConstantFolder<'a> { let mut value_vec: Vec = vec![]; - for (arg_pos, each) in arguments.iter().enumerate() { - let value = &each.0; - let potential_kwarg_value = &each.1; - let mut to_check = &value.clone(); + for (arg_pos, (value, potential_kwarg_value)) in arguments.iter().enumerate() { + let mut to_check = value; // Determines the range based on predetermined colorspace let mut range = match arg_pos { From b6af6a1475c9992aba31928feb3822e35bfcd327 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Thu, 18 Mar 2021 21:35:15 -0700 Subject: [PATCH 22/22] Rearrange initialization --- src/dreammaker/constants.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dreammaker/constants.rs b/src/dreammaker/constants.rs index 6e7db97b..343c4472 100644 --- a/src/dreammaker/constants.rs +++ b/src/dreammaker/constants.rs @@ -843,10 +843,6 @@ impl<'a> ConstantFolder<'a> { } fn rgb(&mut self, args: Vec) -> Result { - if args.len() != 3 && args.len() != 4 && args.len() != 5 { - return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); - } - enum ColorSpace { Rgb = 0, Hsv = 1, @@ -854,9 +850,6 @@ impl<'a> ConstantFolder<'a> { Hcy = 3, } - let mut space = None; - let arguments = self.arguments(args)?; - #[derive(Default)] struct ColorArgs { r: bool, @@ -871,8 +864,15 @@ impl<'a> ConstantFolder<'a> { a: Option, } + if args.len() != 3 && args.len() != 4 && args.len() != 5 { + return Err(self.error(format!("malformed rgb() call, must have 3, 4, or 5 arguments and instead has {}", args.len()))); + } + + let arguments = self.arguments(args)?; + let mut space = None; let mut color_args = ColorArgs::default(); + // Get the value of the `space` kwarg if present, or collect which kwargs are set to automatically determine the color space. for (value, potential_kwarg_value) in &arguments { // Check for kwargs if we're in the right form if let Some(kwarg_value) = potential_kwarg_value {