full graphing

This commit is contained in:
2025-08-31 13:42:48 -05:00
parent b2cc93e47f
commit 659c5f1f84
3 changed files with 137 additions and 41 deletions

View File

@@ -1121,22 +1121,46 @@ short cSpecial::get(eSpecField fld) {
}
}
// TODO oh god the edit stack won't be reflected when graphing on a stack
std::vector<graph_node_t> global_node_graph(std::vector<cSpecial>& globals) {
static cSpecial* get_spec_ref(cScenario& scenario, node_stack_t& stack, size_t which, int town_num_or_out_x = -1, int out_y = -1) {
int mode = (out_y >= 0 ? 1 : (town_num_or_out_x >= 0 ? 2 : 0));
for(auto it = stack.rbegin(); it != stack.rend(); ++it){
if(it->mode == mode && it->which == which)
return &it->node;
}
switch(mode) {
case 0:
if(scenario.scen_specials.size() <= which) return nullptr;
return &scenario.scen_specials[which];
case 1:
if(scenario.outdoors[town_num_or_out_x][out_y]->specials.size() <= which) return nullptr;
return &scenario.outdoors[town_num_or_out_x][out_y]->specials[which];
case 2:
if(scenario.towns[town_num_or_out_x]->specials.size() <= which) return nullptr;
return &scenario.towns[town_num_or_out_x]->specials[which];
default:
throw std::string {"Bad mode"};
}
}
std::vector<graph_node_t> global_node_graph(cScenario& scenario, node_stack_t& stack) {
std::vector<graph_node_t> graph_nodes;
for(int i = 0; i < globals.size(); ++ i){
graph_node_t node = { std::make_pair(true, i) };
// The edit stack might contain new nodes (maybe, I think? Ugh) so I don't think we can use
// the size of the list to determine how far to loop
int i = 0;
cSpecial* next;
while((next = get_spec_ref(scenario, stack, i)) != nullptr){
node_id_t id; id.which = i++;
graph_node_t node = { id };
cSpecial& special = globals[i];
node_properties_t props = *(special.type);
node_properties_t props = *(next->type);
// Forward connections
for(int j = 1; j <= static_cast<int>(eSpecField::JUMP); ++j){
eSpecField fld = static_cast<eSpecField>(j);
if(props.get(special, fld).button == eSpecPicker::NODE){
if(special.get(fld) >= 0){
node.to_nodes.insert(std::make_pair(true, special.get(fld)));
if(props.get(*next, fld).button == eSpecPicker::NODE){
if(next->get(fld) >= 0){
node_id_t id; id.which = next->get(fld);
node.to_nodes.insert(id);
}
// TODO negative numbers can be pointers, which the graph can't follow,
// but that ambiguity or the pointer's identity could be noted on the graph
@@ -1146,27 +1170,84 @@ std::vector<graph_node_t> global_node_graph(std::vector<cSpecial>& globals) {
}
// Backward connections
for(int i = 0; i < globals.size(); ++ i){
for(i = 0; i < graph_nodes.size(); ++i){
graph_node_t& node = graph_nodes[i];
for(node_id id : node.to_nodes){
graph_nodes[id.second].from_nodes.insert(node.id);
for(node_id_t id : node.to_nodes){
graph_nodes[id.which].from_nodes.insert(node.id);
}
}
// Backward connections to Call Global nodes in towns/outdoors
for(int town = 0; town < scenario.towns.size(); ++town){
i = 0;
while((next = get_spec_ref(scenario, stack, i, town)) != nullptr){
if(next->type == eSpecType::CALL_GLOBAL){
node_id_t from; from.which = i; from.town_num_or_out_x = town;
graph_nodes[next->jumpto].from_nodes.insert(from);
}
++i;
}
}
for(int x = 0; x < scenario.outdoors.width(); ++x){
for(int y = 0; y < scenario.outdoors.height(); ++y){
i = 0;
while((next = get_spec_ref(scenario, stack, i, x, y)) != nullptr){
if(next->type == eSpecType::CALL_GLOBAL){
node_id_t from; from.which = i; from.town_num_or_out_x = x; from.out_y = y;
graph_nodes[next->jumpto].from_nodes.insert(from);
}
++i;
}
}
}
return graph_nodes;
}
bool node_compare::operator()(node_id a, node_id b) const {
bool node_compare::operator()(node_id_t a, node_id_t b) const {
// This is just a lexicographical ordering.
if(a.first != b.first) return a.first < b.first;
if(a.second != b.second) return a.second < b.second;
if(a.which != b.which) return a.which < b.which;
if(a.town_num_or_out_x != b.town_num_or_out_x) return a.town_num_or_out_x < b.town_num_or_out_x;
if(a.out_y != b.out_y) return a.out_y < b.out_y;
return false;
}
std::vector<graph_node_t> local_node_graph(std::vector<cSpecial>& locals, std::vector<cSpecial>& globals) {
std::vector<graph_node_t> global_nodes = global_node_graph(globals);
std::vector<graph_node_t> local_node_graph(cScenario& scenario, node_stack_t& stack, int town_num_or_out_x, int out_y) {
std::vector<graph_node_t> graph_nodes;
int i = 0;
cSpecial* next;
while((next = get_spec_ref(scenario, stack, i, town_num_or_out_x, out_y)) != nullptr){
node_id_t id; id.which = i++; id.town_num_or_out_x = town_num_or_out_x; id.out_y = out_y;
graph_node_t node = { id };
std::vector<graph_node_t> local_nodes;
node_properties_t props = *(next->type);
return local_nodes;
// Forward connections
for(int j = 1; j <= static_cast<int>(eSpecField::JUMP); ++j){
eSpecField fld = static_cast<eSpecField>(j);
if(props.get(*next, fld).button == eSpecPicker::NODE){
if(next->get(fld) >= 0){
node_id_t id; id.which = next->get(fld);
if(next->type != eSpecType::CALL_GLOBAL){
id.town_num_or_out_x = town_num_or_out_x;
id.out_y = out_y;
}
node.to_nodes.insert(id);
}
// TODO negative numbers can be pointers, which the graph can't follow,
// but that ambiguity or the pointer's identity could be noted on the graph
}
}
graph_nodes.push_back(node);
}
// Backward connections
for(i = 0; i < graph_nodes.size(); ++i){
graph_node_t& node = graph_nodes[i];
for(node_id_t id : node.to_nodes){
graph_nodes[id.which].from_nodes.insert(node.id);
}
}
return graph_nodes;
}

View File

@@ -18,6 +18,7 @@
namespace legacy { struct special_node_type; };
class cUniverse;
class cScenario;
enum class eSpecField;
static const short SDF_COMPLETE = 250;
@@ -115,19 +116,32 @@ public:
std::string editor_hint(cUniverse& univ) const;
};
typedef std::pair<bool, size_t> node_id;
struct node_id_t {
int which;
int town_num_or_out_x = -1;
int out_y = -1;
};
struct node_compare {
bool operator()(node_id a, node_id b) const;
bool operator()(node_id_t a, node_id_t b) const;
};
struct graph_node_t {
node_id id;
std::set<node_id, node_compare> from_nodes;
std::set<node_id, node_compare> to_nodes;
node_id_t id;
std::set<node_id_t, node_compare> from_nodes;
std::set<node_id_t, node_compare> to_nodes;
};
std::vector<graph_node_t> global_node_graph(std::vector<cSpecial>& globals);
std::vector<graph_node_t> local_node_graph(std::vector<cSpecial>& locals, std::vector<cSpecial&> globals);
struct editing_node_t {
short which, mode;
cSpecial node;
bool is_new;
};
typedef std::vector<editing_node_t> node_stack_t;
std::vector<graph_node_t> global_node_graph(cScenario& scenario, node_stack_t& edit_stack);
std::vector<graph_node_t> local_node_graph(cScenario& scenario, node_stack_t& edit_stack, int town_num_or_out_x, int out_y = -1);
enum class eSpecCtxType {
SCEN, OUTDOOR, TOWN,

View File

@@ -754,14 +754,6 @@ bool edit_area_rect_str(info_rect_t& r) {
// MARK: Special node dialog
struct editing_node_t {
short which, mode;
cSpecial node;
bool is_new;
};
typedef std::vector<editing_node_t> node_stack_t;
static void setup_node_field(cDialog& me, std::string field, short value, const node_function_t& fcn) {
me[field + "-lbl"].setText(fcn.label());
me[field].setTextToNum(value);
@@ -816,14 +808,23 @@ static void put_spec_enc_in_dlog(cDialog& me, node_stack_t& edit_stack) {
cSpecial& spec = edit_stack.back().node;
// Graph fun!
std::vector<graph_node_t> globals = global_node_graph(scenario.scen_specials);
graph_node_t which = globals[edit_stack.back().which];
for(node_id from_id : which.from_nodes){
LOG(fmt::format("{} -> {}", from_id.second, which.id.second));
graph_node_t which;
int mode = edit_stack.back().mode;
if(mode == 0){
std::vector<graph_node_t> globals = global_node_graph(scenario, edit_stack);
which = globals[edit_stack.back().which];
}else if(mode == 1){
std::vector<graph_node_t> locals = local_node_graph(scenario, edit_stack, cur_out.x, cur_out.y);
which = locals[edit_stack.back().which];
}else if(mode == 2){
std::vector<graph_node_t> locals = local_node_graph(scenario, edit_stack, cur_town);
which = locals[edit_stack.back().which];
}
for(node_id to_id : which.to_nodes){
LOG(fmt::format("{} -> {}", which.id.second, to_id.second));
for(node_id_t from_id : which.from_nodes){
LOG(fmt::format("({}, {}, {}) -> ({}, {}, {})", from_id.which, from_id.town_num_or_out_x, from_id.out_y, which.id.which, which.id.town_num_or_out_x, which.id.out_y));
}
for(node_id_t to_id : which.to_nodes){
LOG(fmt::format("({}, {}, {}) -> ({}, {}, {})", which.id.which, which.id.town_num_or_out_x, which.id.out_y, to_id.which, to_id.town_num_or_out_x, to_id.out_y));
}
// Show which node is being edited and what type of node it is