Skip to content

Instantly share code, notes, and snippets.

@Gegell
Created November 1, 2024 17:43
Show Gist options
  • Select an option

  • Save Gegell/fe78dc06baf1bfeba228d3822d450189 to your computer and use it in GitHub Desktop.

Select an option

Save Gegell/fe78dc06baf1bfeba228d3822d450189 to your computer and use it in GitHub Desktop.
Factorio Display Panel Progressbar Generator
from copy import deepcopy
from dataclasses import dataclass
import dataclasses
from typing import Optional
from pyperclip import copy
from base64 import b64encode
import zlib
import matplotlib as mpl
import json
_entity_counter = 0
@dataclass
class PBarConfig:
position: tuple[int, int]
signal_name: str = "parameter-0"
prefix: str = ""
cmap: Optional[callable] = None
length: int = 10 # Number of blocks
step_size: int = 1 # Number of steps between blocks
def json_to_blueprint(json):
json_bytes = json.encode("utf-8")
compressed = zlib.compress(json_bytes, level=9)
return "0" + b64encode(compressed).decode("utf-8")
def linspace(start, stop, num=50):
step = (stop - start) / (num - 1)
return [start + step * i for i in range(num)]
def generate_pbar_conditions(config: PBarConfig):
template = {
"condition": {
"first_signal": {"name": config.signal_name},
"constant": 0, # To be filled in later
"comparator": "=",
},
"icon": {"name": config.signal_name},
"text": "", # To be filled in later
}
# Uses the block chars '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'
substeps = "\u258F\u258E\u258D\u258C\u258B\u258A\u2589\u2588"
# Use border chars '╸', '━'
# substeps = "╸━"
print(f"Using character set: `{substeps}`")
divs = len(substeps)
max_threshold = config.length * divs
steps = list(range(-1, max_threshold, config.step_size))
if steps[-1] < max_threshold - 1:
steps.append(max_threshold - 1)
conditions = []
for step in steps:
num_before = max(step // divs, 0)
num_cur = step % divs
num_after = config.length - num_before
if step == -1:
bar = ""
else:
bar = substeps[-1] * num_before
num_after -= 1
bar += substeps[num_cur]
if config.cmap is not None:
color = config.cmap(step / (max_threshold - 1))
color = mpl.colors.rgb2hex(color)
if bar:
bar = f"[color={color}]{bar}[/color]"
step += 1
postfix = f"{step / (config.length * divs) * 100:>5.1f}%"
num_postfix_prefix = len(postfix) - len(postfix.strip())
postfix = postfix.strip()
end = substeps[-1] * num_after + "0" * num_postfix_prefix
if end:
end = f"[color=#00000000]{end}[/color]"
pbar = bar + end
text = (
f"{config.prefix}[font=technology-slot-level-font]{pbar}[/font] {postfix}"
)
# Fill in the template
cond = deepcopy(template)
cond["condition"]["constant"] = step
cond["text"] = text
conditions.append(cond)
# print(f"{step:<3d}: `{text}`")
# Set the last condition to be >= the max threshold and first to <= 0
conditions[0]["condition"]["comparator"] = "<="
conditions[-1]["condition"]["comparator"] = ">="
return conditions
def generate_pbar_entity(config: PBarConfig):
global _entity_counter
_entity_counter += 1
conditions = generate_pbar_conditions(config)
return {
"entity_number": _entity_counter,
"name": "display-panel",
"position": {
"x": config.position[0] + 0.5,
"y": config.position[1] + 0.5,
},
"direction": 8,
"control_behavior": {
"parameters": conditions,
"text": "",
"icon": {"name": config.signal_name},
},
"always_show": True,
}
def generate_bp_string(configs: list[PBarConfig]):
entities = []
parameters = []
for config in configs:
entities.append(generate_pbar_entity(config))
if "parameter" in config.signal_name:
parameters.append({"type": "id", "id": config.signal_name})
bp_json = {
"blueprint": {
"icons": [{"signal": {"name": "display-panel"}, "index": 1}],
"entities": entities,
"wires": [],
"parameters": parameters,
"item": "blueprint",
"version": 562949954273281,
}
}
return json_to_blueprint(json.dumps(bp_json))
if __name__ == "__main__":
mpl_cmap = mpl.colormaps.get_cmap("RdBu")
def step_cmap(cmap):
return lambda x: cmap(round(x * 10) / 10)
def const_color(hex):
return lambda x: hex
base_config = PBarConfig(
position=(0, 0),
signal_name="parameter-0",
prefix="",
cmap=None,
length=10,
step_size=1,
)
colors = [
("#8e1dcc", "production-science-pack"), # purple
("#e82195", "electromagnetic-science-pack"), # fulgaro pink
("#e94040", "automation-science-pack"), # red science
("#ff9a25", "metallurgic-science-pack"), # vulcanus orange
("#f5e45b", "utility-science-pack"), # yellow science
("#a2b90b", "agricultural-science-pack"), # gleba green
("#55f261", "logistic-science-pack"), # green science
("#3ec5e3", "chemical-science-pack"), # blue science
("#2c46c5", "cryogenic-science-pack"), # aquilo blue
("#29274f", "promethium-science-pack"), # prometheus
("#71788f", "military-science-pack"), # military gray
("#f9f9f9", "space-science-pack"), # space white
]
configs = []
for i, (color, signal_name) in enumerate(colors):
config = dataclasses.replace(
base_config,
position=(0, 2 * i),
signal_name=signal_name,
cmap=const_color(color),
)
configs.append(config)
bp_string = generate_bp_string(configs)
copy(bp_string)
@joshinils
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment