Skip to content

Commit

Permalink
Added map graph legend, updated example
Browse files Browse the repository at this point in the history
  • Loading branch information
TCA166 committed Sep 30, 2024
1 parent 5c87d1f commit 1615678
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Make doc
run: make doc
- name: Add example
run: unzip "examples/TCA166's history.zip" -d target/doc/
run: tar -xJf "examples/TCA166's history.xz" -C target/doc/
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
Expand Down
Binary file added examples/TCA166's history.xz
Binary file not shown.
Binary file removed examples/TCA166's history.zip
Binary file not shown.
4 changes: 2 additions & 2 deletions src/display/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ fn create_graph(data: &Vec<(u32, f64)>, output_path: &str) {
//.caption("Deaths of culture members through time", ("sans-serif", 50).into_font())
.margin(5)
.x_label_area_size(30)
.y_label_area_size(30)
.y_label_area_size(50)
.build_cartesian_2d(min_x..max_x, min_y..(MULT))
.unwrap();

chart.configure_mesh().draw().unwrap();
chart.configure_mesh().y_desc("Percentage of global deaths").draw().unwrap();

chart
.draw_series(LineSeries::new(
Expand Down
8 changes: 6 additions & 2 deletions src/display/localizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ fn demangle_generic(input: &str) -> String {
let mut s = input
.trim_start_matches("dynn_")
.trim_start_matches("nick_")
.trim_end_matches("_perk")
.trim_start_matches("death_")
.trim_start_matches("tenet_")
.trim_start_matches("doctrine_")
Expand All @@ -26,7 +25,12 @@ fn demangle_generic(input: &str) -> String {
.trim_start_matches("b_")
.trim_start_matches("x_x_")
.trim_end_matches("_name")
.replace("_", " ");
.trim_end_matches("_perk");
let mut input_chars = s.chars();
if input_chars.nth(1) == Some('p') && input_chars.nth(3) == Some('_') {
s = s.split_at(4).1;
}
let mut s = s.replace("_", " ");
if s.is_empty() {
return s;
}
Expand Down
57 changes: 49 additions & 8 deletions src/display/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use plotters::{
backend::BitMapBackend,
drawing::IntoDrawingArea,
element::Text,
style::{IntoFont, RGBAColor},
prelude::{EmptyElement, Rectangle},
style::{Color, IntoFont, RGBAColor, ShapeStyle, BLACK},
};

use super::super::parser::{GameId, GameString, SaveFile};
Expand Down Expand Up @@ -55,7 +56,7 @@ fn create_title_province_map(game_path: &str) -> HashMap<String, GameId> {
fn draw_text(img: &mut [u8], width: u32, height: u32, text: &str) {
//TODO is this the best way to draw text?
let back = BitMapBackend::with_buffer(img, (width, height)).into_drawing_area();
let text_height = height as f32 * 0.05;
let text_height = height / 20;
let style = ("sans-serif", text_height)
.into_font()
.color(&RGBAColor(0, 0, 0, 0.5));
Expand All @@ -68,6 +69,45 @@ fn draw_text(img: &mut [u8], width: u32, height: u32, text: &str) {
back.present().unwrap();
}

/// Draws a legend on the given image buffer, the legend is placed at the bottom right corner and consists of a series of colored rectangles with text labels
fn draw_legend(img: &mut [u8], width: u32, height: u32, legend: Vec<(String, [u8; 3])>) {
let back = BitMapBackend::with_buffer(img, (width, height)).into_drawing_area();
let text_height = (height / 30) as i32;
let style = ("sans-serif", text_height).into_font();
let mut x = (width / 50) as i32;
for (label, color) in legend {
let text_size = style.box_size(&label).unwrap();
let margin = text_height / 3;
back.draw(
&(EmptyElement::at((x, height as i32 - (text_height * 2)))
+ Rectangle::new(
[(0, 0), (text_height, text_height)],
ShapeStyle {
color: RGBAColor(color[0], color[1], color[2], 1.0),
filled: true,
stroke_width: 1,
},
)
+ Rectangle::new(
[(0, 0), (text_height, text_height)],
ShapeStyle {
color: BLACK.to_rgba(),
filled: false,
stroke_width: 1,
},
)
+ Text::new(
label,
(text_height + margin, (text_height - text_size.1 as i32)),
style.clone(),
)),
)
.unwrap();
x += text_height + text_size.0 as i32 + (margin * 2);
}
back.present().unwrap();
}

/// A struct representing a game map, from which we can create [crate::structures::Title] maps
pub struct GameMap {
height: u32,
Expand Down Expand Up @@ -207,7 +247,7 @@ impl GameMap {
/// Returns a vector of RGB bytes representing the new map
fn create_map<F>(&self, key_list: Vec<GameString>, assoc: F) -> Vec<u8>
where
F: Fn(&str) -> [u8; 3],
F: Fn(&String) -> [u8; 3],
{
let mut new_map = Vec::with_capacity(self.province_map.len());
let mut colors: HashMap<&[u8], [u8; 3]> = HashMap::new();
Expand Down Expand Up @@ -257,7 +297,7 @@ impl GameMap {
label: &str,
) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
//we need to convert the vec of bytes to a vec of rgba bytes
let mut new_map = self.create_map(key_list, |_: &str| *target_color);
let mut new_map = self.create_map(key_list, |_: &String| *target_color);
if !label.is_empty() {
draw_text(&mut new_map, self.width, self.height, label);
}
Expand All @@ -279,7 +319,7 @@ impl GameMap {
output_path: &str,
label: &str,
) {
let mut new_map = self.create_map(key_list, |_: &str| *target_color);
let mut new_map = self.create_map(key_list, |_: &String| *target_color);
let width = self.width;
let height = self.height;
let output_path = output_path.to_owned();
Expand All @@ -301,11 +341,11 @@ impl GameMap {
}

/// Creates a new map from the province map with the colors of the provinces in id_list changed to a color determined by assoc
pub fn create_map_graph<F>(&self, assoc: F, output_path: &str)
pub fn create_map_graph<F>(&self, assoc: F, output_path: &str, legend: Vec<(String, [u8; 3])>)
where
F: Fn(&str) -> [u8; 3],
F: Fn(&String) -> [u8; 3],
{
let new_map = self.create_map(
let mut new_map = self.create_map(
self.title_color_map
.keys()
.map(|x| GameString::new(x.to_owned()))
Expand All @@ -317,6 +357,7 @@ impl GameMap {
let output_path = output_path.to_owned();
//we move the writing process out into a thread because it's an IO heavy operation
thread::spawn(move || {
draw_legend(&mut new_map, width, height, legend);
save_buffer(
output_path,
&new_map,
Expand Down
2 changes: 2 additions & 0 deletions src/jinja_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const TEMPLATE_NAMES: [&str; 7] = [
/// - no_vis - whether the visualizations are disabled
pub fn create_env(internal: bool, map_present: bool, no_vis: bool) -> Environment<'static> {
let mut env = Environment::new();
env.set_lstrip_blocks(true);
env.set_trim_blocks(true);
env.add_filter("render_ref", render_ref);
env.add_filter("handle_tooltips", handle_tooltips);
env.add_global("map_present", map_present);
Expand Down
30 changes: 21 additions & 9 deletions src/structures/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::{

const TARGET_COLOR: [u8; 3] = [70, 255, 70];
const SECONDARY_COLOR: [u8; 3] = [255, 255, 70];
const BASE_COLOR: [u8; 3] = [255, 255, 255];

/// A struct representing a player in the game
pub struct Player {
Expand Down Expand Up @@ -160,15 +161,23 @@ impl Renderable for Player {
}
}
map.create_map_graph(
|key: &str| {
let mult = sim.get(&key.to_owned());
|key: &String| {
let mult = sim.get(key);
if mult.is_none() {
return [0, 0, 0];
return BASE_COLOR;
} else {
return [255, 255, (255.0 * (1.0 - mult.unwrap())) as u8];
return [
BASE_COLOR[0],
BASE_COLOR[1],
(BASE_COLOR[2] as f32 * (1.0 - mult.unwrap())) as u8,
];
}
},
&format!("{}/sim.png", renderer.get_path()),
vec![
("0%".to_string(), BASE_COLOR),
("100%".to_string(), [BASE_COLOR[0], BASE_COLOR[1], 0]),
],
);
let mut direct_titles = HashSet::new();
let mut descendant_title = HashSet::new();
Expand All @@ -194,18 +203,21 @@ impl Renderable for Player {
target.insert(title.clone());
}
}
//TODO add some sort of title to the map
map.create_map_graph(
|key: &str| {
if direct_titles.contains(&key.to_owned()) {
|key: &String| {
if direct_titles.contains(key) {
return TARGET_COLOR;
} else if descendant_title.contains(&key.to_owned()) {
} else if descendant_title.contains(key) {
return SECONDARY_COLOR;
} else {
return [255, 255, 255];
return BASE_COLOR;
}
},
&format!("{}/dynastyMap.png", renderer.get_path()),
vec![
("Dynastic titles".to_string(), TARGET_COLOR),
("Descendant titles".to_string(), SECONDARY_COLOR),
],
);
}
for char in self.lineage.iter() {
Expand Down

0 comments on commit 1615678

Please sign in to comment.