Line Graph - Flaxbeard/hoi4-scripted-graphs GitHub Wiki

This section assumes general familiarity with scripted GUIs. Refer to the introduction page for resources.

Line Graphs

A line graph is a useful tool to represent information over time. It might be a good way to show economic or demographic trends, and could be a great addition to a custom mechanic.

Using the line graph code from this repo, you can implement your own line graphs. They can be any size or color. Thickness of graph lines are fixed, but can be modified with trivial changes to the shaderfile.

Ingame Example

An example line graph

Here you can see a line graph reading from an array, where new values are continually added.

Implementing a Line Graph

In order to add a line graph to your custom GUI, you will need five components:

  1. Shaderfiles (provided by this repo)
  2. Graphical asset in a .gfx file
  3. A gridboxtype to hold line segments and a containerWindowType to populate the gridbox in your .gui file
  4. Corresponding scripted GUI properties, to set up the dynamic list and adjust segment properties
  5. Arrays holding y-offsets and slopes (in scripted GUI or scripted effect file)

If you learn best by example, take a look at the code samples in this repo. Otherwise, we will examine each section below.

1. Shaderfile

You will need to copy the line graph shaderfile to your mod's /gfx/FX/ directory. If you're curious how the shaderfile works, see below.

2. Graphical Asset

You need to create a progressbartype asset for the line segment. It looks like the following:

progressbartype = {
	name = "GFX_Line_Segment"
	textureFile1 = "/gfx/interface/line_graph/graph_line_full.dds"		# Solid color texture, see dimensional requirements below
	textureFile2 = "/gfx/interface/line_graph/graph_line_empty.dds"		# Blank texture, same size as the above
	size = {
		x = 10		# Should be the distance you want between points
		y = 200		# Needs to be twice the height of your plot
				# Note that the shaderfile expects a y/x ratio of 20:1. The shader will still work, but might not look as good.
	}
	effectFile = "gfx/FX/line_graph.lua"
	horizontal = yes
}

(Note that the shaderfile expects a y/x ratio of 20:1. The shader will still work, but might not look as good otherwise. If you would like to adjust this expectation, you can make a simple tweak to the shaderfile, modifying lines where variables are divided by 20.f. For example, if you had a line segment of size x = 10, y = 300, replace these lines with 30.f.)

3. GUI Components

Adding the line graph to your GUI requires two elements, a gridboxtype container which will hold all your line segments and a containerWindowType which will be used to fill the container at runtime. The gridboxtype is fairly straightforward:

gridboxtype = {
	name = "line_segments"
	position = { x = 99 y = 66 }
	size = { width = 0 height = 200 }		# Height here should correspond with the height of your segments, twice the visual plot height
	slotsize = { width = 10 height = 200 }		# These should be the same as the x and y of your progressbar asset
	max_slots_horizontal = 20			# This should be the width of your plot area divided by the width  of each segment, here 200/10=20
	add_horizontal = yes
}

The container is also fairly basic, but can be modified to make it fancier if you so choose:

containerWindowType = {
	name = "line_graph_line_segment"	# Any name works here, but you will need to use the same name for the dynamic
						# list instantiation in the next step
	size = { width = 10 height = 200 }
	position = { x = 0 y = 0 }

	iconType = {
		name = "segment"
		spriteType = "GFX_Line_Segment"
		position = { x = 0 y = 0 }
	}

	# You may add things like x-axis labels here. See the sample code for an example.
}

Place the container at the top level of your .gui file, not nested in another container.

4. Scripted GUI

In the scripted GUI, you'll need to set up a dynamic list and adjust properties of each line segment. The dynamic list creation is straightforward:

dynamic_lists = {
	line_segments = {
		array = SAMPLE_graph_offsets			# See next section, this is one of the arrays which defines your line graph
		change_scope = no
		entry_container = line_graph_line_segment	# Container name from previous step
		index = segment_idx				# This variable can be used in scripted effects, loc, or GUI to change
								# behavior based on the current segment
	}
}

Next, you'll set the properties of each segment:

properties = {
	# Here, segment is the name of the iconType in your container
	segment = {
		# Sets the relative position of the line segment to the graph offset
		# This graph offset is equal to to -y1
		y = SAMPLE_graph_offsets^segment_idx

		# Tells the shader how steep to draw the line
		# Must be equal to (y2 - y1) / 2 + 50
		frame = SAMPLE_graph_slopes^segment_idx
	}
}

5. Computing Arrays

Finally, you'll want to populate the two arrays which define your line graph. In the example code, these are SAMPLE_graph_offsets and SAMPLE_graph_slopes. The first array is the negated value of the first y position. The second array is the difference (y2 - y1) / 2 + 50, passed in to the shader so it can draw the appropriate slope.

Example code will be added here later, but see the sample for now.

How the Line Graph Works

Refer to the introduction page for an overview of what shaderfiles are and some high level strategies that these graphs employ.

Position of progress bars for each segment

The line graph is made up of one progress bar per line segment, generated by a dynamic list. These progress bars are moved vertically such that the midpoint of the progress bar is at the height of y1. You can see this in the above image.

Then, the shaderfile is provided with the delta in y position between y1 and y2, and draws a line from the midpoint either up or down to match.