Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
a2f4a33c8This file is pinned to the selected release. Download the original to open it in your design tool.
#include "Plait/PlaitApp.h"
#include "AppLib/GLBase.h"
#include "AppLib/AppHost.h"
#include "AppLib/DummyApp.h"
#include "AppLib/GLBase.h"
#include "Plait/CellDB.h"
#include "imgui/imgui.h"
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <deque>
#include <algorithm>
#include <filesystem>
#include <sstream>
#pragma warning(disable:4996)
using namespace std;
const dvec2 node_size = {64,64};
const dvec2 node_half_size = {32,32};
const dvec2 port_size = {4,4};
const double world_width = 65536.0;
const double world_height = 16384.0;
const double half_world_width = world_width / 2.0;
const double half_world_height = world_height / 2.0;
const int world_min = -1;
const int world_max = 1;
static std::map<ToolMode, std::string> tool_to_string = {
{ToolMode::NONE, "NONE"},
{ToolMode::DRAG_NODE, "DRAG_NODE"},
{ToolMode::DRAG_LABEL, "DRAG_LABEL"},
{ToolMode::DRAG_FRAME, "DRAG_FRAME"},
{ToolMode::SELECT_REGION, "SELECT_REGION"},
{ToolMode::GHOST_REGION, "GHOST_REGION"},
{ToolMode::TOGGLE_OLD, "TOGGLE_OLD"},
{ToolMode::TOGGLE_GLOBAL, "TOGGLE_GLOBAL"},
{ToolMode::CREATE_ROOT, "CREATE_ROOT"},
{ToolMode::CREATE_LEAF, "CREATE_LEAF"},
{ToolMode::DELETE_NODE, "DELETE_NODE"},
{ToolMode::LINK_NODE, "LINK_NODE"},
{ToolMode::PAN_VIEW, "PAN_VIEW"},
{ToolMode::MENU_OPTION, "MENU_OPTION"},
};
dvec2 wrap(dvec2 d) {
while(d.x > half_world_width) d.x -= world_width;
while(d.x < -half_world_width) d.x += world_width;
return d;
}
dvec2 wrap_towards(dvec2 origin, dvec2 d) {
while((d.x - origin.x) > half_world_width) d.x -= world_width;
while((d.x - origin.x) < -half_world_width) d.x += world_width;
return d;
}
//---------------------------------------------------------------------------------------------------------------------
int main(int argc, char** argv) {
(void)argc;
(void)argv;
int ret = 0;
PlaitApp* app = new PlaitApp();
(void)app;
LOG_B("Parsing gateboy source\n");
app->die_db.clear();
app->die_db.parse_dir("src\\GateBoyLib");
//app->die_db.save_json("gameboy.die_db.json");
LOG_B("Total cells %d\n", (int)app->die_db.cell_map.size());
LOG_B("Total edges %d\n", (int)app->die_db.traces.size());
LOG_B("Done\n");
#if 0
LOG_B("Loading gameboy.plait.json\n");
nlohmann::json jroot;
std::ifstream("gameboy.plait.json") >> jroot;
app->plait.from_json(jroot, app->die_db);
size_t total_nodes = 0;
for (auto& [tag, plait_cell] : app->plait.cell_map) {
total_nodes++;
total_nodes += plait_cell->leaf_nodes.size();
}
LOG_B("Total cells %zd\n", app->plait.cell_map.size());
LOG_B("Total nodes %zd\n", total_nodes);
LOG_B("Total edges %zd\n", app->plait.traces.size());
LOG_B("Done\n");
AppHost* app_host = new AppHost(app);
ret = app_host->app_main(argc, argv);
delete app;
#endif
return ret;
}
//---------------------------------------------------------------------------------------------------------------------
enum gate_pix_type {
GP_BUF = 0x00000000,
GP_NOT = 0x01000000,
GP_AND = 0x02000000,
GP_NAND = 0x03000000,
GP_OR = 0x04000000,
GP_NOR = 0x05000000,
GP_XOR = 0x06000000,
GP_XNOR = 0x07000000,
GP_PIO = 0x08000000,
GP_POUT = 0x09000000,
GP_PIN = 0x0A000000,
GP_ADD = 0x0B000000,
GP_NOL = 0x0C000000,
GP_NAL = 0x0D000000,
GP_OAN = 0x0E000000,
GP_AO = 0x0F000000,
GP_OA = 0x10000000,
GP_BUS = 0x11000000,
GP_TRI = 0x12000000,
GP_DFFP = 0x13000000,
GP_DFFN = 0x14000000,
GP_MUXP = 0x15000000,
GP_MUXN = 0x16000000
};
PlaitApp::PlaitApp() {
auto& n2c = node_type_to_color;
n2c["not1"] = COL_MID_GREY | GP_NOT;
n2c["and2"] = COL_PALE_RED | GP_AND;
n2c["and3"] = COL_PALE_RED | GP_AND;
n2c["and4"] = COL_PALE_RED | GP_AND;
n2c["nand2"] = COL_PALE_RED | GP_NAND;
n2c["nand3"] = COL_PALE_RED | GP_NAND;
n2c["nand4"] = COL_PALE_RED | GP_NAND;
n2c["nand5"] = COL_PALE_RED | GP_NAND;
n2c["nand6"] = COL_PALE_RED | GP_NAND;
n2c["nand7"] = COL_PALE_RED | GP_NAND;
n2c["or2"] = COL_PALE_BLUE | GP_OR;
n2c["or3"] = COL_PALE_BLUE | GP_OR;
n2c["or4"] = COL_PALE_BLUE | GP_OR;
n2c["nor2"] = COL_PALE_BLUE | GP_NOR;
n2c["nor3"] = COL_PALE_BLUE | GP_NOR;
n2c["nor4"] = COL_PALE_BLUE | GP_NOR;
n2c["nor5"] = COL_PALE_BLUE | GP_NOR;
n2c["nor6"] = COL_PALE_BLUE | GP_NOR;
n2c["nor7"] = COL_PALE_BLUE | GP_NOR;
n2c["nor8"] = COL_PALE_BLUE | GP_NOR;
n2c["and_or3"] = COL_MID_MAGENTA | GP_AO;
n2c["or_and3"] = COL_MID_MAGENTA | GP_OA;
n2c["not_or_and3"] = COL_MID_MAGENTA | GP_OAN;
n2c["xor2"] = COL_MINT | GP_XOR;
n2c["xnor2"] = COL_MINT | GP_XNOR;
n2c["mux2n"] = COL_ROSE | GP_MUXN;
n2c["mux2p"] = COL_ROSE | GP_MUXP;
n2c["amux2"] = COL_ROSE | GP_MUXP;
n2c["amux4"] = COL_ROSE | GP_MUXP;
}
PlaitApp::~PlaitApp() {
}
const char* PlaitApp::app_get_title() {
return "PlaitApp";
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::paint_node(PlaitNode* node) {
node->color = 0xFFFF00FF;
auto cell_type = node->plait_cell->die_cell->cell_type;
if (cell_type == DieCellType::PIN_IN) node->color = COL_PALE_RED | GP_PIN;
if (cell_type == DieCellType::PIN_OUT) node->color = COL_PALE_GREEN | GP_POUT;
if (cell_type == DieCellType::PIN_IO) node->color = COL_PALE_YELLOW | GP_PIO;
if (cell_type == DieCellType::SIG_IN) node->color = COL_MID_RED | GP_PIN;
if (cell_type == DieCellType::SIG_OUT) node->color = COL_MID_GREEN | GP_POUT;
if (cell_type == DieCellType::BUS) node->color = COL_PALE_GREEN | GP_BUS;
if (cell_type == DieCellType::DFF) node->color = COL_PALE_BROWN | GP_DFFP;
if (cell_type == DieCellType::LATCH) node->color = COL_ORANGE | GP_NAL;
if (cell_type == DieCellType::TRIBUF) node->color = COL_MID_YELLOW | GP_TRI;
if (cell_type == DieCellType::ADDER) node->color = COL_MID_TEAL | GP_ADD;
if (cell_type == DieCellType::LOGIC) {
auto it = node_type_to_color.find(node->plait_cell->gate());
if (it != node_type_to_color.end()) {
node->color = (*it).second;
}
else {
LOG_R("Could not pick a color for %s\n", node->plait_cell->gate());
}
}
//node->color = 0x00FFFFFF;
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::app_init(int screen_w, int screen_h) {
dvec2 screen_size(screen_w, screen_h);
view_control.init(screen_size);
box_painter.init();
port_painter.init();
grid_painter.init(world_width, world_height);
edge_painter.init();
outline_painter.init();
text_painter.init();
ui_text_painter.init();
blitter.init();
dump_painter.init_ascii();
check_gl_error();
for (auto& [tag, cell] : plait.cell_map) {
paint_node(cell->core_node);
for (auto& [name, root] : cell->root_nodes) {
paint_node(root);
}
for (auto& [name, leaf] : cell->leaf_nodes) {
paint_node(leaf);
}
}
LOG_B("Init done %f\n", timestamp());
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::app_close() {
}
//---------------------------------------------------------------------------------------------------------------------
bool PlaitApp::hit_node(dvec2 _mouse_pos, PlaitNode* node) {
dvec2 node_pos = node->get_pos_new();
double min_x = node_pos.x;
double min_y = node_pos.y;
double max_x = node_pos.x + node_size.x;
double max_y = node_pos.y + node_size.y;
while(_mouse_pos.x - min_x > half_world_width) _mouse_pos.x -= world_width;
while(_mouse_pos.x - max_x < -half_world_width) _mouse_pos.x += world_width;
if (_mouse_pos.x >= min_x &&
_mouse_pos.y >= min_y &&
_mouse_pos.x <= max_x &&
_mouse_pos.y <= max_y) {
return true;
}
else {
return false;
}
}
PlaitNode* PlaitApp::pick_node(dvec2 _mouse_pos_world) {
for (auto& [tag, plait_cell] : plait.cell_map) {
if (hit_node(_mouse_pos_world, plait_cell->core_node)) return plait_cell->core_node;
for (auto& [name, root] : plait_cell->root_nodes) {
if (hit_node(_mouse_pos_world, root)) return root;
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
if (hit_node(_mouse_pos_world, leaf)) return leaf;
}
}
return nullptr;
}
PlaitLabel* PlaitApp::pick_label(dvec2 _mouse_pos) {
for (auto label : plait.labels) {
dvec2 min = label->pos_new;
dvec2 max = label->pos_new + dvec2(label->text.size() * 6 * label->scale, 12 * label->scale);
if (_mouse_pos.x >= min.x &&
_mouse_pos.y >= min.y &&
_mouse_pos.x <= max.x &&
_mouse_pos.y <= max.y) {
return label;
}
}
return nullptr;
}
PlaitFrame* PlaitApp::pick_frame(dvec2 _mouse_pos) {
for (auto frame : plait.frames) {
dvec2 min = frame->pos;
dvec2 glyph_size(6, 12);
dvec2 max = min + dvec2(frame->size) * glyph_size * (double)frame->text_scale;
if (_mouse_pos.x >= min.x &&
_mouse_pos.y >= min.y &&
_mouse_pos.x <= max.x &&
_mouse_pos.y <= max.y) {
return frame;
}
}
return nullptr;
}
//---------------------------------------------------------------------------------------------------------------------
bool PlaitApp::contains_node(dvec2 corner_a, dvec2 corner_b, PlaitNode* node) {
dvec2 rmin = min(corner_a, corner_b);
dvec2 rmax = max(corner_a, corner_b);
dvec2 nmin = node->get_pos_new();
dvec2 nmax = node->get_pos_new() + node_size;
if (nmin.x < rmin.x) return false;
if (nmin.y < rmin.y) return false;
if (nmax.x > rmax.x) return false;
if (nmax.y > rmax.y) return false;
return true;
}
int PlaitApp::apply_region_node(dvec2 corner_a, dvec2 corner_b, NodeCallback callback) {
int hit = 0;
for (auto& [tag, plait_cell] : plait.cell_map) {
if (contains_node(corner_a, corner_b, plait_cell->core_node)) {
callback(plait_cell->core_node);
hit++;
}
for (auto& [name, root] : plait_cell->root_nodes) {
if (contains_node(corner_a, corner_b, root)) {
callback(root);
hit++;
}
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
if (contains_node(corner_a, corner_b, leaf)) {
callback(leaf);
hit++;
}
}
}
// If we just clicked a thing instead of click-drag-selecting, fire the callback for only
// the clicked node.
if (hit == 0) {
if (clicked_node) {
callback(clicked_node);
}
}
return hit;
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::select_region(dvec2 corner_a, dvec2 corner_b) {
//LOG_B("Selection region ");
auto callback = [this](PlaitNode* node) {
//LOG_B("%s ", node->plait_cell->name());
node->select();
node_selection.insert(node);
};
apply_region_node(corner_a, corner_b, callback);
//LOG_B("\n");
}
void PlaitApp::select_node(PlaitNode* node) {
//LOG_B("Selecting %s\n", node->plait_cell->name());
node->select();
node_selection.insert(node);
}
void PlaitApp::commit_selection() {
if (node_selection.empty()) return;
//printf("Committing ");
for (auto node : node_selection) {
//LOG_B("%s ", node->plait_cell->name());
node->commit_pos();
}
//LOG_B("\n");
}
void PlaitApp::revert_selection() {
if (node_selection.empty()) return;
//LOG_B("Reverting ");
for (auto node : node_selection) {
//LOG_B("%s ", node->plait_cell->name());
node->revert_pos();
}
//LOG_B("\n");
}
void PlaitApp::clear_selection() {
if (node_selection.empty()) return;
//LOG_B("Unselecting ");
for (auto node : node_selection) {
//LOG_B("%s ", node->plait_cell->name());
node->deselect();
}
node_selection.clear();
//LOG_B("\n");
}
//---------------------------------------------------------------------------------------------------------------------
double remap(double x, double a1, double a2, double b1, double b2) {
x = (x - a1) / (a2 - a1);
x = x * (b2 - b1) + b1;
return x;
}
double remap_clamp(double x, double a1, double a2, double b1, double b2) {
if (x < a1) x = a1;
if (x > a2) x = a2;
x = (x - a1) / (a2 - a1);
x = x * (b2 - b1) + b1;
return x;
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::event_menu_option(SDL_Event event) {
switch(event.type) {
case SDL_KEYDOWN: {
int key = event.key.keysym.scancode;
if (key == SDL_SCANCODE_S) {
const char* filename = "gameboy.plait.json";
LOG_B("Saving plait %s\n", filename);
commit_selection();
clear_selection();
nlohmann::json jroot;
plait.to_json(jroot);
std::ofstream(filename) << jroot.dump(2);
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_LALT) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_select_region(SDL_Event event) {
bool was_drag = length(click_pos_screen - mouse_pos_screen) > 3;
switch(event.type) {
case SDL_MOUSEBUTTONDOWN:
if (clicked_node) {
select_node(clicked_node);
}
break;
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
if (was_drag) {
commit_selection();
clear_selection();
select_region(click_pos_wrap, mouse_pos_world);
}
else {
if (clicked_node) {
select_node(clicked_node);
}
}
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_LCTRL) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_ghost_region(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
auto callback = [this](PlaitNode* node) {
node->toggle_ghosted();
};
apply_region_node(click_pos_wrap, mouse_pos_world, callback);
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_Q) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_toggle_old(SDL_Event event) {
switch (event.type) {
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
auto callback = [this](PlaitNode* node) {
node->toggle_old();
};
apply_region_node(click_pos_wrap, mouse_pos_world, callback);
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_F) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_toggle_global(SDL_Event event) {
switch (event.type) {
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
auto callback = [this](PlaitNode* node) {
node->toggle_global();
};
apply_region_node(click_pos_wrap, mouse_pos_world, callback);
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_G) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_pan_view(SDL_Event event) {
bool was_drag = length(click_pos_screen - mouse_pos_screen) > 3;
switch(event.type) {
case SDL_MOUSEMOTION: {
if (event.motion.state & SDL_BUTTON_LMASK) {
view_control.pan(dvec2(event.motion.xrel, event.motion.yrel));
}
break;
}
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
if (!was_drag) {
commit_selection();
clear_selection();
}
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_drag_nodes(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEMOTION: {
if (event.motion.state & SDL_BUTTON_LMASK) {
dvec2 pos_abs_new = (mouse_pos_world - click_pos_world) + click_pos_wrap + clicked_node_offset;
pos_abs_new.x = round(pos_abs_new.x / 96) * 96.0;
pos_abs_new.y = round(pos_abs_new.y / 48) * 48.0;
dvec2 delta = pos_abs_new - clicked_node->get_pos_new();
for (auto node : node_selection) node->move(delta);
}
break;
}
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_drag_label(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEMOTION: {
if (event.motion.state & SDL_BUTTON_LMASK) {
dvec2 pos_abs_new = mouse_pos_wrap + clicked_label_offset;
pos_abs_new.x = round(pos_abs_new.x / 16) * 16.0;
pos_abs_new.y = round(pos_abs_new.y / 16) * 16.0;
clicked_label->pos_old = pos_abs_new;
clicked_label->pos_new = pos_abs_new;
}
break;
}
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_drag_frame(SDL_Event event) {
assert(clicked_frame);
switch (event.type) {
case SDL_MOUSEMOTION: {
if (event.motion.state & SDL_BUTTON_LMASK) {
dvec2 pos_abs_new = mouse_pos_wrap + clicked_frame_offset;
pos_abs_new.x = round(pos_abs_new.x / 16) * 16.0;
pos_abs_new.y = round(pos_abs_new.y / 16) * 16.0;
clicked_frame->pos = pos_abs_new;
}
break;
}
case SDL_KEYDOWN: {
break;
}
case SDL_MOUSEBUTTONUP: {
if (event.button.button & SDL_BUTTON_LMASK) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_imgui(SDL_Event event) {
ImGuiIO& io = ImGui::GetIO();
switch (event.type) {
case SDL_TEXTINPUT: {
io.AddInputCharactersUTF8(event.text.text);
break;
}
case SDL_KEYDOWN:
case SDL_KEYUP: {
int key = event.key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event.type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
io.KeySuper = false;
break;
}
case SDL_MOUSEWHEEL: {
io.MouseWheelH += event.wheel.x;
io.MouseWheel += event.wheel.y;
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_create_root(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEBUTTONDOWN: {
if (event.button.button & SDL_BUTTON_LMASK) {
commit_selection();
clear_selection();
if (clicked_node) {
plait.spawn_root_node(clicked_node);
clicked_node = nullptr;
}
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_Z) {
release_tool();
}
break;
}
}
}
void PlaitApp::event_create_leaf(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEBUTTONDOWN: {
if (event.button.button & SDL_BUTTON_LMASK) {
commit_selection();
clear_selection();
if (clicked_node) {
plait.spawn_leaf_node(clicked_node);
clicked_node = nullptr;
}
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_X) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_link_node(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEBUTTONDOWN: {
if (event.button.button & SDL_BUTTON_LMASK) {
if (clicked_node) {
for (auto leaf_node : node_selection) {
plait.link_nodes(leaf_node, clicked_node);
}
}
clicked_node = nullptr;
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_C) {
release_tool();
}
break;
}
}
}
//--------------------------------------------------------------------------------
void PlaitApp::event_delete_node(SDL_Event event) {
switch(event.type) {
case SDL_MOUSEBUTTONDOWN: {
if (event.button.button & SDL_BUTTON_LMASK) {
commit_selection();
clear_selection();
if (clicked_node) {
if (clicked_node->name != "core") {
plait.delete_node(clicked_node);
}
clicked_node = nullptr;
}
}
break;
}
case SDL_KEYUP: {
if (event.key.keysym.scancode == SDL_SCANCODE_V) {
release_tool();
}
break;
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::event_select_tool(SDL_Event event) {
if (event.type == SDL_KEYDOWN) {
int key = event.key.keysym.scancode;
ToolMode new_tool = ToolMode::NONE;
if (key == SDL_SCANCODE_E) show_edges = !show_edges;
if (key == SDL_SCANCODE_F) new_tool = ToolMode::TOGGLE_OLD;
if (key == SDL_SCANCODE_G) new_tool = ToolMode::TOGGLE_GLOBAL;
if (key == SDL_SCANCODE_Q) new_tool = ToolMode::GHOST_REGION;
if (key == SDL_SCANCODE_Z) new_tool = ToolMode::CREATE_ROOT;
if (key == SDL_SCANCODE_X) new_tool = ToolMode::CREATE_LEAF;
if (key == SDL_SCANCODE_C) new_tool = ToolMode::LINK_NODE;
if (key == SDL_SCANCODE_V) new_tool = ToolMode::DELETE_NODE;
if (key == SDL_SCANCODE_LALT) new_tool = ToolMode::MENU_OPTION;
if (key == SDL_SCANCODE_LCTRL) new_tool = ToolMode::SELECT_REGION;
if (key == SDL_SCANCODE_ESCAPE && node_selection.size()) {
//LOG_B("Reverting selection\n");
revert_selection();
clear_selection();
}
if (key == SDL_SCANCODE_RETURN && node_selection.size()) {
//LOG_B("Commiting selection\n");
commit_selection();
clear_selection();
}
if (new_tool != ToolMode::NONE) {
current_tool = new_tool;
}
}
else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button & SDL_BUTTON_LMASK) {
auto new_clicked_label = pick_label(mouse_pos_world);
if (clicked_node) {
clicked_node_offset = wrap(clicked_node->get_pos_new() - mouse_pos_world);
if (!clicked_node->selected()) {
commit_selection();
clear_selection();
select_node(clicked_node);
}
current_tool = ToolMode::DRAG_NODE;
}
else if (clicked_frame) {
if (event.button.clicks == 2) {
selected_frame = clicked_frame;
}
else {
clicked_frame_offset = wrap(clicked_frame->pos - mouse_pos_world);
current_tool = ToolMode::DRAG_FRAME;
}
}
else if (new_clicked_label) {
clicked_label = new_clicked_label;
clicked_label_offset = clicked_label->pos_new - mouse_pos_world;
current_tool = ToolMode::DRAG_LABEL;
}
else {
clicked_label = nullptr;
current_tool = ToolMode::PAN_VIEW;
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::app_update(dvec2 screen_size, double delta_time) {
(void)delta_time;
auto& view = view_control.view_smooth_snap;
time_delta = delta_time;
double time_start = timestamp();
int mouse_x = 0, mouse_y = 0;
mouse_buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
mouse_pos_screen = {mouse_x, mouse_y};
mouse_pos_world = view.screen_to_world(mouse_pos_screen, screen_size);
mouse_pos_wrap = mouse_pos_world;
while(mouse_pos_wrap.x > half_world_width) mouse_pos_wrap.x -= world_width;
while(mouse_pos_wrap.x < -half_world_width) mouse_pos_wrap.x += world_width;
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[0] = (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
io.MouseDown[1] = (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
io.MousePos = { (float)mouse_pos_screen.x, (float)mouse_pos_screen.y };
bool imgui_override = ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) || io.WantCaptureKeyboard || io.WantCaptureMouse;
SDL_Event event;
while (SDL_PollEvent(&event)) {
// If ImGui wants input, it gets all the SDL events.
if (imgui_override) {
event_imgui(event);
continue;
}
if (event.type == SDL_MOUSEBUTTONDOWN && (event.button.button & SDL_BUTTON_LMASK)) {
clicked_frame = pick_frame(mouse_pos_world);
clicked_node = pick_node(mouse_pos_world);
click_pos_screen = mouse_pos_screen;
click_pos_world = mouse_pos_world;
click_pos_wrap = mouse_pos_wrap;
}
if (event.type == SDL_KEYDOWN) {
int key = event.key.keysym.scancode;
if (key == SDL_SCANCODE_DELETE) {
if (clicked_label) {
plait.labels.erase(std::find(plait.labels.begin(), plait.labels.end(), clicked_label));
delete clicked_label;
clicked_label = nullptr;
}
}
if (key == SDL_SCANCODE_F1) {
show_help = !show_help;
}
}
switch(current_tool) {
case ToolMode::NONE: event_select_tool(event); break;
case ToolMode::DRAG_NODE: event_drag_nodes(event); break;
case ToolMode::DRAG_LABEL: event_drag_label(event); break;
case ToolMode::DRAG_FRAME: event_drag_frame(event); break;
case ToolMode::SELECT_REGION: event_select_region(event); break;
case ToolMode::GHOST_REGION: event_ghost_region(event); break;
case ToolMode::TOGGLE_OLD: event_toggle_old(event); break;
case ToolMode::TOGGLE_GLOBAL: event_toggle_global(event); break;
case ToolMode::CREATE_ROOT: event_create_root(event); break;
case ToolMode::CREATE_LEAF: event_create_leaf(event); break;
case ToolMode::LINK_NODE: event_link_node(event); break;
case ToolMode::DELETE_NODE: event_delete_node(event); break;
case ToolMode::PAN_VIEW: event_pan_view(event); break;
case ToolMode::MENU_OPTION: event_menu_option(event); break;
default: {
LOG_R("Bad tool!\n");
debugbreak();
break;
}
}
if(event.type == SDL_MOUSEWHEEL) {
view_control.on_mouse_wheel(mouse_pos_screen, screen_size, double(event.wheel.y) * 0.25);
}
if (event.type == SDL_MOUSEBUTTONUP && (event.button.button & SDL_BUTTON_LMASK)) {
clicked_frame = nullptr;
clicked_node = nullptr;
}
}
if (imgui_override) {
hovered_node = nullptr;
hovered_frame = nullptr;
}
else {
hovered_node = pick_node(mouse_pos_world);
hovered_frame = pick_frame(mouse_pos_world);
}
view_control.update(delta_time);
//----------------------------------------
// Pull nodes towards their parents/children
#if 0
for (auto& [tag, node] : plait.node_map) {
for (auto next : node->next) {
spring_nodes(node, next);
}
if (node->get_cell() && node->get_cell()->cell_type == CellType::BUS) {
if (node->prev.size() >= 2) {
for (size_t i = 0; i < node->prev.size() - 1; i++) {
spring_nodes2(node->prev[i + 0], node->prev[i + 1]);
}
}
}
else {
if (node->next.size() >= 2) {
for (size_t i = 0; i < node->next.size() - 1; i++) {
spring_nodes2(node->next[i + 0], node->next[i + 1]);
}
}
}
}
#endif
#if 0
// Push nodes away from their siblings
for (auto node : plait.nodes) {
if (node->prev.size() >= 2) {
for (size_t i = 0; i < node->prev.size() - 1; i++) {
Node* a = node->prev[i + 0];
Node* b = node->prev[i + 1];
if (a->selected) continue;
if (a->locked) continue;
if (a->anchored()) continue;
dvec2 offset = a->get_pos_new() - b->get_pos_new();
if (length(offset) < 256) {
a->set_pos_new(a->get_pos_old() + offset * 0.01);
a->commit_pos();
}
}
}
if (node->next.size() >= 2) {
for (size_t i = 0; i < node->next.size() - 1; i++) {
Node* a = node->next[i + 0];
Node* b = node->next[i + 1];
if (a->selected) continue;
if (a->locked) continue;
if (a->anchored()) continue;
dvec2 offset = a->get_pos_new() - b->get_pos_new();
if (length(offset) < 256) {
a->set_pos_new(a->get_pos_old() + offset * 0.01);
a->commit_pos();
}
}
}
}
#endif
//----------------------------------------
// Apply accumulated spring forces
#if 0
for (auto& [tag, node] : plait.node_map) {
if (node->selected || node->locked || node->anchored()) {
}
else {
node->move(node->spring_force * 0.01);
node->commit_pos();
}
node->spring_force = {0,0};
}
#endif
time_update = timestamp() - time_start;
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::draw_node_fill(PlaitNode* node, uint32_t color) {
if (!node->ghosted) {
/*
bool bad_node = false;
if (node->is_leaf() && node->plait_cell->die_cell->fanout == 1 && !node->old) {
bad_node = true;
}
if (node->is_root() && node->plait_cell->die_cell->input_ports.size() == 1) {
bad_node = true;
}
if (bad_node) {
bad_node_count++;
color = (frame_count & 16) ? 0xFFFF00FF : 0xFF00FFFF;
}
*/
if (node->old) {
color = (color & 0xFF000000) | ((color & 0x00FEFEFE) >> 1);
}
box_painter.push_corner_size(node->get_pos_new(), node_size, color);
}
}
//----------------------------------------
void PlaitApp::draw_node_ports(PlaitNode* node) {
dvec2 node_pos_new = node->get_pos_new();
// Node input port(s)
size_t prev_port_count = node->plait_cell->die_cell->input_ports.size();
double prev_stride = (node_size.y) / (prev_port_count + 1);
for (size_t i = 0; i < prev_port_count; i++) {
dvec2 port_pos = node_pos_new + dvec2(2, prev_stride * (i + 1));
port_painter.push_center_size(port_pos, port_size, 0x00008000);
}
// Node output port(s)
size_t next_port_count = node->plait_cell->die_cell->output_ports.size();
double next_stride = (node_size.y) / (next_port_count + 1);
for (size_t i = 0; i < next_port_count; i++) {
dvec2 port_pos = node_pos_new + dvec2(node_size.x - 2, next_stride * (i + 1));
port_painter.push_center_size(port_pos, port_size, 0x00000080);
}
}
//----------------------------------------
static char buf[256];
void PlaitApp::draw_node_text(PlaitNode* node) {
dvec2 node_pos_new = node->get_pos_new();
text_painter.add_text_at_simple(node->plait_cell->name(), float(node_pos_new.x), float(node_pos_new.y));
//text_painter.add_text_at_simple(node->plait_cell->gate(), float(node_pos_new.x + 8), float(node_pos_new.y + 24));
//sprintf_s(buf, 256, "%d : %d", (int)node->plait_cell->root_nodes.size(), (int)node->plait_cell->leaf_nodes.size());
//text_painter.add_text_at(buf, float(node_pos_new.x + 8), float(node_pos_new.y + 40));
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::draw_edge(PlaitTrace* edge) {
auto output_node = edge->output_node;
auto input_node = edge->input_node;
//if (!output_node->visible && !input_node->visible) return;
if (output_node->ghosted || input_node->ghosted) return;
auto output_pos_new = output_node->get_pos_new();
auto input_pos_new = input_node->get_pos_new();
//auto delta = wrap(input_pos_new - output_pos_new);
auto delta = input_pos_new - output_pos_new;
input_pos_new = output_pos_new + delta;
// Highlight "backwards" edges in red.
bool edge_backwards = delta.x < 0;
uint32_t output_color = edge_backwards ? 0xFF0000FF : 0x8044FF44;
uint32_t input_color = edge_backwards ? 0xFF0000FF : 0x8044FF44;
// Make edges connected to selected nodes opaque.
if (output_node->plait_cell->selected() || input_node->plait_cell->selected()) {
if (edge_backwards) {
output_color = 0xFF8080FF;
input_color = 0xFF8080FF;
}
else {
output_color |= 0xFF000000;
input_color |= 0xFF000000;
}
}
// Old nodes can drive other old nodes and DFF D ports.
// New nodes can only drive other new nodes, except DFF D ports.
auto& cell_type = edge->input_node->plait_cell->die_cell->cell_type;
auto& input_port_name = edge->input_node->plait_cell->die_cell->input_ports[edge->input_port_index];
bool input_is_logic = (cell_type == DieCellType::ADDER) || (cell_type == DieCellType::LOGIC);
bool input_is_dff = cell_type == DieCellType::DFF;
bool input_is_dff_d = input_is_dff && (input_port_name[0] == 'D');
bool bad_edge = false;
// Old node -> old node = input must be a logic cell
if ( edge->output_node->old && edge->input_node->old) bad_edge = !input_is_logic;
// Old node -> new node = input must be dff d port
if ( edge->output_node->old && !edge->input_node->old) bad_edge = !input_is_dff_d;
// New node -> old node = always bad
if (!edge->output_node->old && edge->input_node->old) bad_edge = true;
// New node -> new node = input must _not_ be dff d port
if (!edge->output_node->old && !edge->input_node->old) bad_edge = input_is_dff_d;
if (bad_edge) {
input_color = (frame_count & 16) ? 0xFFFF00FF : 0xFF00FFFF;
output_color = (frame_count & 16) ? 0xFFFF00FF : 0xFF00FFFF;
bad_edge_count++;
}
size_t output_port_count = output_node->plait_cell->die_cell->output_ports.size();
size_t input_port_count = input_node->plait_cell->die_cell->input_ports.size();
double output_stride = (node_size.y) / (output_port_count + 1);
double input_stride = (node_size.y) / (input_port_count + 1);
dvec2 output_port_pos = output_pos_new + dvec2(node_size.x, output_stride * (edge->output_port_index + 1));
dvec2 input_port_pos = input_pos_new + dvec2(0, input_stride * (edge->input_port_index + 1));
edge_painter.push(output_port_pos,
output_color,
input_port_pos,
input_color);
}
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::app_render_frame(dvec2 screen_size, double delta) {
(void)delta;
auto& view = view_control.view_smooth_snap;
double time_start = timestamp();
bad_node_count = 0;
bad_edge_count = 0;
bad_branch_count = 0;
//----------------------------------------
// Grid layer
grid_painter.render(view, screen_size);
//----------------------------------------
// Text blocks
for (auto f : plait.frames) {
const char* cursor = f->text.c_str();
auto grid_bytes = f->size.x * f->size.y;
text_grid.resize(grid_bytes);
memset(text_grid.data(), 0, grid_bytes);
int cursor_x2 = 0;
int cursor_y2 = 0;
while (*cursor) {
char c = *cursor++;
if (c == '\n') {
cursor_x2 = 0;
cursor_y2++;
}
else if (c == '\t') {
while (cursor_x2 % 2) cursor_x2++;
}
else {
if (cursor_x2 < f->size.x && cursor_y2 < f->size.y) {
text_grid[cursor_x2 + cursor_y2 * f->size.x] = c;
}
cursor_x2++;
}
}
dump_painter.dump(view, screen_size, f->pos.x, f->pos.y, f->text_scale, f->text_scale, f->size.x, f->size.y, f->color, (uint8_t*)text_grid.data());
}
//----------------------------------------
// Visibility
/*
for (auto& [tag, plait_cell] : plait.cell_map) {
plait_cell->core_node->update_visibility(view);
for (auto& [name, root] : plait_cell->root_nodes) {
root->update_visibility(view);
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
leaf->update_visibility(view);
}
}
*/
//----------------------------------------
// Node layer
if (node_selection.size()) {
// Selected node shadows
for (auto node: node_selection) {
box_painter.push_corner_size(node->get_pos_old(), node_size, 0xFF000000);
}
// Selected node highlights
for (auto node: node_selection) {
box_painter.push_corner_size(node->get_pos_new() - dvec2(6,6), node_size + dvec2(12,12), 0xFFFFFFFF);
}
}
box_painter.render(view, screen_size, 0, 0, 1);
//----------
// Node fills
for (auto& [tag, plait_cell] : plait.cell_map) {
auto core = plait_cell->core_node;
uint32_t color = core->color;
if (core->is_global()) {
box_painter.push_corner_size(core->get_pos_new() - dvec2(3, 3), node_size + dvec2(6, 6), 0xFF00FFFF);
for (auto& [name, root] : plait_cell->root_nodes) {
box_painter.push_corner_size(root->get_pos_new() - dvec2(3, 3), node_size + dvec2(6, 6), 0xFF00BBFF);
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
box_painter.push_corner_size(leaf->get_pos_new() - dvec2(3, 3), node_size + dvec2(6, 6), 0xFF00FF88);
}
}
draw_node_fill(plait_cell->core_node, color);
draw_node_text(plait_cell->core_node);
draw_node_ports(plait_cell->core_node);
for (auto& [name, root] : plait_cell->root_nodes) {
draw_node_fill(root, color);
draw_node_text(root);
draw_node_ports(root);
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
draw_node_fill(leaf, color);
draw_node_text(leaf);
draw_node_ports(leaf);
}
}
if (show_edges) {
//for (auto& [trace_key, trace] : plait.trace_map_old) {
for (auto& plait_trace : plait.traces) {
draw_edge(plait_trace);
}
}
box_painter.render(view, screen_size, 0, 0, 1);
port_painter.render(view, screen_size, 0, 0, 1);
text_painter.render(view, screen_size, 0, 0);
edge_painter.render(view, screen_size, 0, 0, 1);
//----------
// Branches
for (auto& [tag, plait_cell] : plait.cell_map) {
auto core = plait_cell->core_node;
auto die_cell = plait_cell->die_cell;
auto core_pos = core->pos_new;
bool no_branches = false;
//no_branches |= die_cell->cell_type == DieCellType::BUS;
//no_branches |= die_cell->cell_type == DieCellType::SIG_IN;
//no_branches |= die_cell->cell_type == DieCellType::SIG_OUT;
//no_branches |= die_cell->cell_type == DieCellType::PIN_IN;
//no_branches |= die_cell->cell_type == DieCellType::PIN_OUT;
//no_branches |= die_cell->cell_type == DieCellType::PIN_IO;
no_branches |= core->is_global();
if (hovered_node && hovered_node->plait_cell == plait_cell) no_branches = false;
bool wrap_ok = false;
if (die_cell->cell_type == DieCellType::BUS) wrap_ok = true;
if (die_cell->cell_type == DieCellType::PIN_IO) wrap_ok = true;
double max_root_x = -1e10;
double min_leaf_x = 1e10;
for (auto& [name, root] : plait_cell->root_nodes) {
if (root->pos_new.x > max_root_x) max_root_x = root->pos_new.x;
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
if (leaf->pos_new.x < min_leaf_x) min_leaf_x = leaf->pos_new.x;
}
// Draw all branches for selected nodes that aren't buses
if (!no_branches) {
uint32_t color = plait_cell->selected_node_count ? 0xFFFFFF00 : 0x60FFFF00;
if (hovered_node && hovered_node->plait_cell == plait_cell) color = 0xFFFFFF00;
for (auto& [name, root] : plait_cell->root_nodes) {
if (root->ghosted) continue;
if (root->old && !plait_cell->selected_node_count && (root != hovered_node)) continue;
auto root_pos = root->pos_new;
edge_painter.push(core_pos + node_half_size, color, root_pos + node_half_size, color);
}
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
if (leaf->ghosted) continue;
if (leaf->old && !plait_cell->selected_node_count && (leaf != hovered_node)) continue;
auto leaf_pos = leaf->pos_new;
edge_painter.push(core_pos + node_half_size, color, leaf_pos + node_half_size, color);
}
}
// Draw bad root node branches
for (auto& [name, root] : plait_cell->root_nodes) {
if (root->ghosted) continue;
auto root_pos = root->pos_new;
bool bad_branch = false;
if (root_pos.x > min_leaf_x) bad_branch = true;
if (!plait_cell->core_node->old && root_pos.x > core_pos.x) bad_branch = true;
if (wrap_ok) bad_branch = false;
if (root->old) bad_branch = false;
if (!bad_branch) continue;
bad_branch_count++;
edge_painter.push(core_pos + node_half_size, 0x8000FFFF, root_pos + node_half_size, 0x8000FFFF);
}
// Draw bad leaf node branches
for (auto& [name, leaf] : plait_cell->leaf_nodes) {
if (leaf->ghosted) continue;
auto leaf_pos = leaf->pos_new;
bool bad_branch = false;
if (leaf_pos.x < max_root_x) bad_branch = true;
if (!plait_cell->core_node->old && leaf_pos.x < core_pos.x) bad_branch = true;
if (wrap_ok) bad_branch = false;
if (leaf->old) bad_branch = false;
if (!bad_branch) continue;
bad_branch_count++;
edge_painter.push(core_pos + node_half_size, 0x8000FFFF, leaf_pos + node_half_size, 0x8000FFFF);
}
}
edge_painter.render(view, screen_size, 0, 0, 1);
//----------------------------------------
// Labels
for (auto label : plait.labels) {
uint32_t color = (label == clicked_label) ? COL_VIOLET : COL_PALE_GREY;
color |= 0xFF000000;
text_painter.add_text_at(label->text.c_str(), color, label->pos_new.x, label->pos_new.y, label->scale);
}
text_painter.render(view, screen_size, 0, 0);
//----------------------------------------
// Selection rect
if (mouse_buttons & SDL_BUTTON_LMASK) {
uint32_t sel_color = 0x00000000;
if (current_tool == ToolMode::SELECT_REGION) sel_color = 0xFFFFFFFF;
if (current_tool == ToolMode::GHOST_REGION) sel_color = 0xFFFF0080;
if (current_tool == ToolMode::TOGGLE_GLOBAL) sel_color = 0xFF00FFFF;
if (current_tool == ToolMode::TOGGLE_OLD) sel_color = 0xFF888888;
if (sel_color) {
outline_painter.push_box(click_pos_wrap, mouse_pos_wrap, sel_color);
outline_painter.render(view, screen_size, 0, 0, 1);
}
}
//----------------------------------------
// Done
frame_count++;
time_render = timestamp() - time_start;
}
//---------------------------------------------------------------------------------------------------------------------
static const char* help_text = R"(========== Legend ==========
Graph edges and "root" nodes are generated from the parsed source and cannot be created or destroyed.
Root nodes can be cloned to move their input/output edges elsewhere in the graph.
Logic cells:
Blue : OR/NOR
Pink : AND/NAND
Green : XOR/XNOR
Teal : 1-Bit Adders
Grey : NOT
Magenta : Mux, And+Or, Or+And, Nor+And
Storage cells:
Orange : Latches
Brown : Flip-flops
Communication cells:
Red : Input signals (from the CPU)
Pink : Input Pins
Yellow : Tri-state bus drivers, IO pins
Green : Buses, Output pins, Output signals
Node modifiers:
No highlight : Local node
Orange highlight : Input clone of a global node
Yellow highlight : Global root node.
Green highlight : Output clone of a global node.
Dimmed : An "old" signal from the previous cycle
Solid black : Hidden "ghost" node.
Edge colors:
Green : Links between local nodes.
Teal : Links between local clones.
Yellow : Node clones are "backwards"
Red : Node edges are "backwards"
Blink : Invalid connection between "old" and "new" node
Flip-flop node input ports placement:
| Clock Non-inverting output |
| Set (optional) |
| Reset (optional) Inverting output |
| Data |
Flip-flop data input ports _must_ be connected to an _old_ node.
========== Controls ==========
Click+drag the background grid to pan the view, mousewheel to zoom the view
Hold a key and click or click-drag to:
No key : Move selected nodes or text boxes
Control: Select nodes
F : Toggle "old" state
G : Toggle "global" state
Q : Toggle "ghost" state
Hold a key and click a node to:
Z : Create a new "input" clone
X : Create a new "output" clone
C : Link all selected nodes to it if possible
V : Delete it.
Other keys:
E : Toggle edge visibility
Alt+S : Save graph
Escape : Unselect nodes and return them to their previous location
Enter : Unselect nodes and keep them in their new location
Broken stuff:
Double-clicking a text box will open an editor, but controls aren't finished yet.
)";
//---------------------------------------------------------------------------------------------------------------------
void PlaitApp::app_render_ui(dvec2 screen_size, double delta) {
(void)delta;
double time_start = timestamp();
if (!show_help) {
ui_text_painter.bg_col = vec4(0, 0, 0, 0.3);
static StringDumper d;
d.clear();
d("\002========== PRESS F1 FOR HELP ==========\001\n");
d("World zoom : %f\n", view_control.view_smooth.view_zoom());
d("World ppw : %f\n", view_control.view_smooth.scale_world_to_screen());
d("World scale : %f\n", view_control.view_smooth.scale_screen_to_world());
d("Bad node count : %d\n", bad_node_count);
d("Bad edge count : %d\n", bad_edge_count);
d("Bad branch count : %d\n", bad_branch_count);
d("Mouse screen x : %f\n", mouse_pos_screen.x);
d("Mouse screen y : %f\n", mouse_pos_screen.y);
d("Mouse world x : %f\n", mouse_pos_world.x);
d("Mouse world y : %f\n", mouse_pos_world.y);
d("Mouse wrap x : %f\n", mouse_pos_wrap.x);
d("Mouse wrap y : %f\n", mouse_pos_wrap.y);
d("\n");
d("Click screen x : %f\n", click_pos_screen.x);
d("Click screen y : %f\n", click_pos_screen.y);
d("Click world x : %f\n", click_pos_world.x);
d("Click world y : %f\n", click_pos_world.y);
d("Click wrap x : %f\n", click_pos_wrap.x);
d("Click wrap y : %f\n", click_pos_wrap.y);
d("Click offset x : %f\n", clicked_node_offset.x);
d("Click offset y : %f\n", clicked_node_offset.y);
d("\n");
d("Tool mode %s\n", tool_to_string[current_tool].c_str());
d("Selected nodes : ");
for (auto selected_node : node_selection) d("%s ", selected_node->plait_cell->name());
d("\n");
if (clicked_node) {
d("Clicked node : %s %f %f \n", clicked_node->plait_cell->name(), clicked_node->pos_new.x, clicked_node->pos_new.y);
}
else {
d("Clicked node : \n");
}
if (hovered_node) {
d("Hovered node : %s %f %f\n", hovered_node->plait_cell->name(), hovered_node->pos_new.x, hovered_node->pos_new.y);
}
else {
d("Hovered node : \n");
}
if (hovered_frame) {
d("Hovered frame : %s %f %f\n", hovered_frame->title.c_str(), hovered_frame->pos.x, hovered_frame->pos.y);
}
else {
d("Hovered frame : \n");
}
if (clicked_frame) {
d("Clicked frame : %s %f %f\n", clicked_frame->title.c_str(), clicked_frame->pos.x, clicked_frame->pos.y);
}
else {
d("Clicked frame : \n");
}
if (selected_frame) {
d("Selected frame : %s %f %f\n", selected_frame->title.c_str(), selected_frame->pos.x, selected_frame->pos.y);
}
else {
d("Selected frame : \n");
}
d("\n");
d("Clicked label : %s\n", clicked_label ? clicked_label->text.c_str() : "<none>");
d("Delta time : %f\n", 1000.0 * time_delta);
d("Update time : %f\n", 1000.0 * time_update);
d("Graph render time : %f\n", 1000.0 * time_render);
d("UI render time : %f\n", 1000.0 * time_ui);
if (hovered_node) {
hovered_node->plait_cell->dump(d);
hovered_node->dump(d);
}
ui_text_painter.add_text_at(d.c_str(), 8, 8);
}
else {
ui_text_painter.bg_col = vec4(0, 0, 0, 0.8);
ui_text_painter.add_text_at(help_text, 8, 8);
}
ui_text_painter.render(Viewport::screenspace(screen_size), screen_size, 0, 0);
time_ui = timestamp() - time_start;
if (selected_frame) {
auto f = selected_frame;
ImGui::OpenPopup("Frame Editor");
if (ImGui::BeginPopupModal("Frame Editor", NULL, 0))
{
static ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
auto string_resize = [](ImGuiInputTextCallbackData* data) {
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
std::string* str = (std::string*)data->UserData;
str->resize(data->BufSize);
data->Buf = str->data();
}
return 0;
};
if (ImGui::InputText("title", f->title.data(), f->title.size(), flags, string_resize, &f->title)) {
}
if (ImGui::InputTextMultiline("text", f->text.data(), f->text.size(), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags, string_resize, &f->text)) {
}
ImGui::SliderInt("size x", &f->size.x, 1, 128);
ImGui::SliderInt("size y", &f->size.y, 1, 128);
ImGui::SliderInt("scale", &f->text_scale, 1, 32);
ImGui::ColorEdit4("Background", (float*)&f->color);
if (ImGui::Button("Close")) {
ImGui::CloseCurrentPopup();
selected_frame = nullptr;
}
ImGui::EndPopup();
}
}
if (0) {
ImGui::Begin("Label Editor");
/*
static char str0[128] = "Label Text Here";
if (clicked_label) {
strcpy(str0, clicked_label->text.c_str());
}
*/
/*
if (ImGui::InputText("text", str0, IM_ARRAYSIZE(str0))) {
if (clicked_label) {
clicked_label->text = str0;
}
}
*/
/*
if (ImGui::Button("Create label")) {
//LOG_B("Creating label %s\n", str0);
PlaitLabel* label = new PlaitLabel {
str0,
view_control.view_smooth_snap.world_center(),
view_control.view_smooth_snap.world_center(),
16.0
};
plait.labels.push_back(label);
}
*/
//static char node_tag[128];
//ImGui::InputText("tag", node_tag, IM_ARRAYSIZE(node_tag));
/*
if (ImGui::Button("Find Tag")) {
//LOG_B("find tag\n");
for (auto& [tag, plait_cell] : plait.cell_map) {
if (tag == node_tag) {
view_control.center_on(plait_cell->core_node->pos_old + node_size * 0.5);
}
}
}
*/
ImGui::End();
}
}
//---------------------------------------------------------------------------------------------------------------------