/* Hurry Curry! - a game about cooking Copyright (C) 2025 Hurry Curry! Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::diagram_svg::node_color; use anyhow::{Result, bail}; use hurrycurry_protocol::{ Gamedata, Message, book::{Diagram, DiagramNode, NodeStyle}, }; use std::{ fmt::Write, io::Write as W2, process::{Command, Stdio}, }; pub fn diagram_dot_svg(data: &Gamedata, diagram: &Diagram) -> Result { let mut child = Command::new("dot") .arg("-Tsvg") .arg("-Knop2") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn()?; let dot = diagram_dot(data, diagram, true)?; child.stdin.as_mut().unwrap().write_all(dot.as_bytes())?; let output = child.wait_with_output()?; if !output.status.success() { bail!("dot failed"); } Ok(String::from_utf8(output.stdout)?) } pub fn diagram_dot(data: &Gamedata, diagram: &Diagram, use_position: bool) -> Result { let mut out = String::new(); writeln!(out, "digraph {{")?; for (i, n) in diagram.nodes.iter().enumerate() { let mut attrs = Vec::new(); if use_position { attrs.push(format!("pos=\"{},{}!\"", n.position.x, n.position.y)); } node_style(&mut attrs, &n); match &n.label { Message::Text(text) => { attrs.push(format!("label=\"{text}\"")); } Message::Item(item_index) => { attrs.push(format!( "image=\"/tmp/items/{}.png\"", data.item_name(*item_index) )); attrs.push("imagescale=true".to_owned()); attrs.push("width=1".to_owned()); attrs.push("height=1".to_owned()); attrs.push("fixedsize=true".to_owned()); attrs.push("label=\"\"".to_owned()); } Message::Tile(tile_index) => { attrs.push(format!( "image=\"/tmp/tiles/{}.png\"", data.tile_name(*tile_index) )); attrs.push("imagescale=true".to_owned()); attrs.push("width=1".to_owned()); attrs.push("height=1".to_owned()); attrs.push("fixedsize=true".to_owned()); attrs.push("label=\"\"".to_owned()); } _ => unimplemented!(), } writeln!(out, "k{i} [{}]", attrs.join(" "))?; } for edge in &diagram.edges { writeln!(out, "k{} -> k{}", edge.src, edge.dst)?; } writeln!(out, "}}")?; Ok(out) } fn node_style(attrs: &mut Vec, node: &DiagramNode) { let shape = match node.style { NodeStyle::FinalProduct => "circle", NodeStyle::IntermediateProduct => "circle", NodeStyle::ProcessActive => "box", NodeStyle::ProcessPassive => "box", NodeStyle::ProcessInstant => "box", }; let color = node_color(node); attrs.push(format!("shape={shape}")); attrs.push("style=filled".to_owned()); attrs.push(format!("fillcolor=\"{color}\"")); attrs.push(format!("color=\"{color}\"")); }