From e45d049f7202024ac91f2fd0828678e457979470 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Tue, 28 Nov 2023 17:59:58 -0800 Subject: [PATCH] Wrote tests for layout scaling. This revealed the extent of my errors, which was vast. Fixes followed. Probably. --- layouts/square/LayoutV2.toml | 323 +++++++++++++++++++++++++---------- src/layouts/layout_v1.rs | 47 ++++- src/layouts/layout_v2.rs | 49 +++++- src/layouts/mod.rs | 13 ++ 4 files changed, 329 insertions(+), 103 deletions(-) diff --git a/layouts/square/LayoutV2.toml b/layouts/square/LayoutV2.toml index 805171d1..90979334 100644 --- a/layouts/square/LayoutV2.toml +++ b/layouts/square/LayoutV2.toml @@ -1,9 +1,38 @@ -global_scale = 0.0 -anchor_name = "none" -hide_ammo_when_irrelevant = false -hide_left_when_irrelevant = false -font = "" -font_size = 0.0 +# THIS IS A TEST FIXTURE. + +global_scale = 2.0 + +# A named location for the HUD. Use this as a shortcut for common anchor +# points. These work no matter what the player's screen resolution is, because this +# is turned into a location point at run-time. +# Values: bottom_left, bottom_right, top_left, top_right, center, +# center_top, center_bottom, left_center, right_center +anchor_name = "bottom_left" + +# You can also specify the anchor point like this if a named anchor point +# doesn't work for your layout. These values are NOT scaled. +# anchor = { x = 2100.0, y = 825.0 } + +# The size of the HUD bounding box. This is used to help place the HUD on the screen +# but is not enforced in any way. (The HUD isn't clipped to this region, for instance.) +size = { x = 190.0, y = 250.0 } + +# Only draw the ammo slot when a weapon that uses ammo (ranged) is equipped. +hide_ammo_when_irrelevant = true +# Hide the left hand slot when a ranged weapon is equipped. This lets you +# use the same location as the left hand to show ammo. +hide_left_when_irrelevant = true + +# The file containing the typeface to use. Must be truetype. Must be in resources/fonts. +font = "futura-book-bt.ttf" +# A font sizing hint to the font loader about what size most of the text will use. +# Text rendered at this size will look best. This value is scaled before the font is loaded. +# Text alignment calculations are made using this font size. +font_size = 18.0 + +# Enable any of these you need text rendering for. +# You will need to supply a true-type font with the glyphs; futura doesn't have them. +# The "Inter" font packaged with the i18 layout does have them. chinese_full_glyphs = false simplified_chinese_glyphs = false cyrillic_glyphs = false @@ -12,115 +41,227 @@ korean_glyphs = false thai_glyphs = false vietnamese_glyphs = false -[anchor] -x = 0.0 -y = 0.0 +# An optional background image. If your layout doesn't have an overall background, you +# can safely leave this out. All image elements look like this. +[background] +# This is the file name of an SVG that must be in resources/backgrounds. +svg = "hud_bg.svg" +# The size to draw the image. +size = { x = 190.0 , y = 250.0} +# The color to draw the image with. If alpha is zero, the background is not drawn. +color = { r = 0, g = 0, b = 0, a = 64 } -[size] -x = 0.0 -y = 0.0 +# ----- right hand slot +# The slot showing what's in the right hand. [right] -text = [] +# Where the center of this element is relative to the HUD anchor. +offset = { x = 375.0, y = 0.0 } -[right.offset] -x = 0.0 -y = 0.0 +# Optional slot background image: if not present, not drawn. You can have +# different background images for different slots if you like. +[right.background] +svg = "slot_bg.svg" +size = { x = 100.0 , y = 100.0} +color = { r = 255, g = 255, b = 255, a = 128 } -[right.icon.size] -x = 0.0 -y = 0.0 +# the icon element of this slot. Color might be overridden if +# the user has "colorize icons" enabled. +[right.icon] +size = { x = 60.0, y = 60.0 } +offset = { x = 0.0, y = 0.0 } +color = { r = 200, g = 200, b = 200, a = 255 } -[right.icon.offset] -x = 0.0 -y = 0.0 +# Optional display for the slot's hotkey. All hotkey elements look like this. +[right.hotkey] +size = { x = 30.0, y = 30.0 } +offset = { x = -50.0, y = 0.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +# An optional background image for the hotkey. You can leave this out to not +# draw a background for the hotkey display. +[right.hotkey.background] +svg = "hotkey_bg.svg" +size = { x = 30.0 , y = 30.0} +color = { r = 255, g = 255, b = 255, a = 128 } -[right.icon.color] -r = 255 -g = 255 -b = 255 -a = 255 +# An array of text elements for this slot. You can have as many +# as you want, but each one costs time to draw so keep the count low. +# The text can include values filled in from the item's info. To +# fill in a value, name it surrounded with curly braces, like the +# example. +# Possible values: count, name, kind, +[[right.text]] +alignment = "right" +offset = { x = 0.0, y = 0.0 } +color = { r = 255, g = 255, b = 255, a = 0 } +font_size = 18.0 +contents = "{count} {name}" -[left] -text = [] +# Optional burn time/enchant charge bar. NOT YET IMPLEMENTED. +[right.progress_bar] +offset = { x = 20.0, y = 20.0 } +background = "bar_bg.svg" +color = { r = 255, g = 255, b = 255, a = 255 } +size = { x = 10.0 , y = 100.0 } # this is how to scale the bar, irrespective of orientation +orientation = "horizontal" # defaults to vertical -[left.offset] -x = 0.0 -y = 0.0 +# NOT YET IMPLEMENTED. +[right.progress_arc] +# this might be an option -[left.icon.size] -x = 0.0 -y = 0.0 +# NOT YET IMPLEMENTED. +[right.progress_circle] +# same -[left.icon.offset] -x = 0.0 -y = 0.0 +# NOT YET IMPLEMENTED. +[right.poison] +offset = { x = 20.0, y = 20.0 } +svg = "poison.svg" +color = {r = 160, g = 240, b = 2, a = 255 } # same color as poison. consider using the color names? +size = { x = 10.0, y = 10.0 } -[left.icon.color] -r = 255 -g = 255 -b = 255 -a = 255 +# ----- left hand slot -[power] -text = [] +[left] +offset = { x = 250.0, y = 0.0 } + +[left.background] +svg = "slot_bg.svg" +size = { x = 100.0 , y = 100.0} +color = { r = 255, g = 255, b = 255, a = 128 } -[power.offset] -x = 0.0 -y = 0.0 +[left.icon] +size = { x = 60.0, y = 60.0 } +offset = { x = 0.0, y=0.0 } +color = {r = 200, g = 200, b = 200, a = 255} -[power.icon.size] -x = 0.0 -y = 0.0 +[left.hotkey] +size = { x = 30.0, y = 30.0 } +offset = { x = -50.0, y = 0.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +[left.hotkey.background] +svg = "hotkey_bg.svg" +size = { x = 30.0 , y = 30.0} +color = { r = 255, g = 255, b = 255, a = 128 } -[power.icon.offset] -x = 0.0 -y = 0.0 +[[left.text]] +alignment = "left" +offset = { x = -50.0, y = 55.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +font_size = 18.0 +contents = "{count} {name}" -[power.icon.color] -r = 255 -g = 255 -b = 255 -a = 255 +# ----- utility slot [utility] -text = [] +offset = { x = 125.0, y = 0.0 } +[utility.background] +svg = "slot_bg.svg" +size = { x = 100.0 , y = 100.0} +color = { r = 255, g = 255, b = 255, a = 128 } +[utility.icon] +size = { x = 60.0, y = 60.0 } +offset = { x = 0.0, y=0.0 } +color = {r = 200, g = 200, b = 200, a = 255} +[utility.hotkey] +size = { x = 30.0, y = 30.0 } +offset = { x = -50.0, y = 0.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +[utility.hotkey.background] +svg = "hotkey_bg.svg" +size = { x = 30.0 , y = 30.0} +color = { r = 255, g = 255, b = 255, a = 128 } +[[utility.text]] +alignment = "left" +offset = { x = 15.0, y = 15.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +font_size = 24.0 +contents = "{count}" +[[utility.text]] +alignment = "left" +offset = { x = -50.0, y = 55.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +font_size = 18.0 +contents = "{name}" -[utility.offset] -x = 0.0 -y = 0.0 +# ----- power slot -[utility.icon.size] -x = 0.0 -y = 0.0 +[power] +offset = { x = 0.0, y = 0.0 } +[power.background] +svg = "slot_bg.svg" +size = { x = 100.0 , y = 100.0} +color = { r = 255, g = 255, b = 255, a = 128 } +[power.icon] +size = { x = 60.0, y = 60.0 } +offset = { x = 0.0, y=0.0 } +color = {r = 200, g = 200, b = 200, a = 255} +[power.hotkey] +size = { x = 30.0, y = 30.0 } +offset = { x = -50.0, y = 0.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +[power.hotkey.background] +svg = "hotkey_bg.svg" +size = { x = 30.0 , y = 30.0} +color = { r = 255, g = 255, b = 255, a = 128 } -[utility.icon.offset] -x = 0.0 -y = 0.0 +[[power.text]] +alignment = "center" +offset = { x = 0.0, y = 55.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +font_size = 18.0 +contents = "{name}" -[utility.icon.color] -r = 255 -g = 255 -b = 255 -a = 255 +# ----- ammo slot [ammo] -text = [] - -[ammo.offset] -x = 0.0 -y = 0.0 - -[ammo.icon.size] -x = 0.0 -y = 0.0 +offset = { x = 250.0, y = 0.0 } +[ammo.background] +svg = "slot_bg.svg" +size = { x = 100.0 , y = 100.0 } +color = { r = 255, g = 255, b = 255, a = 128 } +[ammo.icon] +color = { r = 200, g = 200, b = 200, a = 255 } +size = { x = 60.0, y = 60.0 } +offset = { x = 0.0, y=0.0 } +[ammo.hotkey] +size = { x = 30.0, y = 30.0 } +offset = { x = -50.0, y = 0.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +# Note that this slot separates count and name, and uses +# a text element for each. +[[ammo.text]] +alignment = "left" +contents = "{count}" +color = { r = 200, g = 200, b = 200, a = 255 } +font_size = 24.0 +offset = { x = 15.0, y = 15.0 } +[[ammo.text]] +alignment = "left" +contents = "{name}" +offset = { x = -50, y = 55.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +font_size = 18.0 -[ammo.icon.offset] -x = 0.0 -y = 0.0 +# ----- equipset slot -[ammo.icon.color] -r = 255 -g = 255 -b = 255 -a = 255 +[equipset] +align_text = "center" +offset = { x = 0.0, y = -125.0 } +[equipset.background] +svg = "slot_bg.svg" +size = { x = 100.0 , y = 100.0 } +color = { r = 255, g = 255, b = 255, a = 128 } +[equipset.icon] +color = { r = 200, g = 200, b = 200, a = 255 } +size = { x = 60.0, y = 60.0 } +offset = { x = 0.0, y=0.0 } +[equipset.hotkey] +color = { r = 255, g = 255, b = 255, a = 255 } +offset = { x = -50.0, y = 0.0 } +size = { x = 30.0, y = 30.0 } +[[equipset.text]] +contents = "{name}" +offset = { x = 0.0, y = -75.0 } +color = { r = 255, g = 255, b = 255, a = 255 } +font_size = 18.0 diff --git a/src/layouts/layout_v1.rs b/src/layouts/layout_v1.rs index a3da7fb1..49d4f2aa 100644 --- a/src/layouts/layout_v1.rs +++ b/src/layouts/layout_v1.rs @@ -140,7 +140,7 @@ impl HudLayout1 { fn flatten(&self, slot: &SlotLayout) -> SlotFlattened { let anchor = self.anchor_point(); - let center = slot.offset.translate(&anchor).scale(self.global_scale); + let center = anchor.translate(&slot.offset.scale(self.global_scale)); let mut text = Vec::new(); if slot.name_color.a > 0 { @@ -198,7 +198,7 @@ impl From<&HudLayout1> for LayoutFlattened { LayoutFlattened { global_scale: v.global_scale, anchor: v.anchor_point(), - size: v.size.clone(), + size: v.size.scale(v.global_scale), bg_size: Point { x: v.size.x * v.global_scale, y: v.size.y * v.global_scale, @@ -253,11 +253,11 @@ mod tests { fn hexagon_tb_valid() { let data = include_str!("../../layouts/hexagons/SoulsyHUD_hexagons_tb.toml"); let specific: HudLayout1 = - toml::from_str(data).expect("minimal layout should be valid toml"); + toml::from_str(data).expect("hexagons_tb layout should be valid toml"); assert_eq!(specific.anchor_name, NamedAnchor::BottomRight); - let minimal: Layout = + let hexagonal: Layout = toml::from_str(data).expect("serde should figure out which layout schema"); - match minimal { + match hexagonal { Layout::Version2(_) => unreachable!(), Layout::Version1(ref v) => { assert_eq!(v.anchor_name, NamedAnchor::BottomRight); @@ -265,8 +265,43 @@ mod tests { assert_eq!(v.anchor_point().y, 1290.0); } } - let flattened = minimal.flatten(); + let flattened = hexagonal.flatten(); assert_eq!(flattened.anchor.x, 3290.0); assert_eq!(flattened.anchor.y, 1290.0); } + + #[test] + fn flattening_applies_scale() { + let data = include_str!("../../layouts/hexagons/SoulsyHUD_hexagons_tb.toml"); + let layout: HudLayout1 = toml::from_str(data).expect("minimal layout should be valid toml"); + assert_eq!(layout.global_scale, 0.5); + assert_eq!(layout.size, Point { x: 600.0, y: 600.0 }); + assert_eq!(layout.font_size, 37.0); + assert_eq!(layout.layouts[0].size, Point { x: 200.0, y: 200.0 }); + let anchor = layout.anchor_point(); + let right_original = layout + .layouts + .iter() + .find(|slot| slot.element == HudElement::Right) + .expect("layout expected to have a right hud element") + .clone(); + + // if the above assertions succeed, these should too. + let flattened = Layout::Version1(Box::new(layout)).flatten(); + assert_eq!(flattened.size, Point { x: 300.0, y: 300.0 }); + assert_eq!(flattened.bg_size, Point { x: 300.0, y: 300.0 }); + assert_eq!(flattened.font_size, 18.5); + assert_eq!(flattened.anchor, anchor); + assert_eq!(flattened.slots[0].bg_size, Point { x: 100.0, y: 100.0 }); + let right_slot = flattened + .slots + .iter() + .find(|slot| slot.element == HudElement::Right) + .expect("the right slot must be present"); + let slot_center = Point { + x: flattened.anchor.x + (right_original.offset.x * flattened.global_scale), + y: flattened.anchor.y + (right_original.offset.y * flattened.global_scale), + }; + assert_eq!(right_slot.center, slot_center); + } } diff --git a/src/layouts/layout_v2.rs b/src/layouts/layout_v2.rs index 937ddc36..ebd2e4ed 100644 --- a/src/layouts/layout_v2.rs +++ b/src/layouts/layout_v2.rs @@ -109,7 +109,7 @@ impl HudLayout2 { let hkbg = hotkey.background.unwrap_or_default(); let anchor = self.anchor_point(); - let center = slot.offset.translate(&anchor).scale(self.global_scale); + let center = anchor.translate(&slot.offset.scale(self.global_scale)); let text = slot .text .iter() @@ -119,7 +119,7 @@ impl HudLayout2 { SlotFlattened { element, center: center.clone(), - bg_size: bg.size, + bg_size: bg.size.scale(self.global_scale), bg_color: bg.color, bg_image: bg.svg, icon_size: slot.icon.size.scale(self.global_scale), @@ -233,14 +233,14 @@ impl From<&HudLayout2> for LayoutFlattened { LayoutFlattened { global_scale: v.global_scale, anchor: v.anchor_point(), - size: v.size.clone(), - bg_size: bg.size.clone(), + size: v.size.scale(v.global_scale), + bg_size: bg.size.scale(v.global_scale), bg_color: bg.color.clone(), bg_image: bg.svg.clone(), hide_ammo_when_irrelevant: v.hide_ammo_when_irrelevant, hide_left_when_irrelevant: v.hide_left_when_irrelevant, font: v.font.clone(), - font_size: v.font_size, + font_size: v.font_size * v.global_scale, chinese_full_glyphs: v.chinese_full_glyphs, simplified_chinese_glyphs: v.simplified_chinese_glyphs, cyrillic_glyphs: v.cyrillic_glyphs, @@ -256,7 +256,7 @@ impl From<&HudLayout2> for LayoutFlattened { #[cfg(test)] mod tests { use super::*; - use crate::layouts::Layout; + use crate::layouts::{resolutionHeight, Layout}; // #[test] // #[ignore] @@ -339,4 +339,41 @@ mod tests { } } } + + #[test] + fn flattening_applies_scale() { + let data = include_str!("../../layouts/square/LayoutV2.toml"); + let layout: HudLayout2 = + toml::from_str(data).expect("square text fixture should be valid toml"); + assert_eq!(layout.global_scale, 2.0); + assert_eq!(layout.size, Point { x: 190.0, y: 250.0 }); + assert_eq!(layout.font_size, 18.0); + assert_eq!(layout.right.offset, Point { x: 375.0, y: 0.0 }); + let anchor = layout.anchor_point(); + assert_eq!( + anchor, + Point { + x: 190.0, + y: resolutionHeight() - layout.size.y + } + ); + + // if the above assertions succeed, these should too. + let flattened = Layout::Version2(Box::new(layout.clone())).flatten(); + assert_eq!(flattened.size, Point { x: 380.0, y: 500.0 }); + assert_eq!(flattened.bg_size, Point { x: 380.0, y: 500.0 }); + assert_eq!(flattened.font_size, 36.0); + assert_eq!(flattened.anchor, anchor); + assert_eq!(flattened.slots[0].bg_size, Point { x: 200.0, y: 200.0 }); + let right_slot = flattened + .slots + .iter() + .find(|slot| slot.element == HudElement::Right) + .expect("the right slot must be present"); + let slot_center = Point { + x: flattened.anchor.x + (layout.right.offset.x * flattened.global_scale), + y: flattened.anchor.y + (layout.right.offset.y * flattened.global_scale), + }; + assert_eq!(right_slot.center, slot_center); + } } diff --git a/src/layouts/mod.rs b/src/layouts/mod.rs index 3b4a8d7c..3387e07a 100644 --- a/src/layouts/mod.rs +++ b/src/layouts/mod.rs @@ -240,6 +240,19 @@ mod tests { use super::shared::NamedAnchor; use super::*; + #[test] + fn point_functions_behave() { + let point = Point { x: 10.0, y: 15.0 }; + let puncta = Point { x: -5.0, y: 2.0 }; + assert_eq!(point.translate(&puncta), puncta.translate(&point)); + assert_eq!(point.scale(6.0), Point { x: 60.0, y: 90.0 }); + assert_eq!(puncta.scale(-2.0), Point { x: 10.0, y: -4.0 }); + assert_eq!( + puncta.scale(-2.0).translate(&puncta), + Point { x: 5.0, y: -2.0 } + ); + } + #[test] fn can_lazy_load_layouts() { let layout = hud_layout();