From 409ff0f49aa1432d68a09a90b4095ec534053a17 Mon Sep 17 00:00:00 2001 From: Tanasart <22589759+Ttanasart-pt@users.noreply.github.com> Date: Mon, 29 May 2023 13:06:05 +0200 Subject: [PATCH] Graph image exporter --- .../draw_sprite_ext_override.gml | 14 ++ scripts/panel_graph/panel_graph.gml | 16 +- .../panel_graph_export_image.gml | 62 +++++++- .../panel_graph_export_image_dialog.gml | 138 ++++++++++++++++-- scripts/panel_preview/panel_preview.gml | 11 +- 5 files changed, 214 insertions(+), 27 deletions(-) diff --git a/scripts/draw_sprite_ext_override/draw_sprite_ext_override.gml b/scripts/draw_sprite_ext_override/draw_sprite_ext_override.gml index 4f11a371b..c879c44fa 100644 --- a/scripts/draw_sprite_ext_override/draw_sprite_ext_override.gml +++ b/scripts/draw_sprite_ext_override/draw_sprite_ext_override.gml @@ -5,6 +5,20 @@ function draw_sprite_ext_override(spr, ind, _x, _y, xscale = 1, yscale = 1, rot __draw_sprite_ext(spr, ind, round(_x), round(_y), xscale, yscale, rot, color, alpha); } +#macro draw_sprite_stretched_ext draw_sprite_stretched_ext_override +#macro __draw_sprite_stretched_ext draw_sprite_stretched_ext + +function draw_sprite_stretched_ext_override(spr, ind, _x, _y, w = 1, h = 1, color = c_white, alpha = 1) { + __draw_sprite_stretched_ext(spr, ind, round(_x), round(_y), round(w), round(h), color, alpha); +} + +#macro draw_sprite_stretched draw_sprite_stretched_override +#macro __draw_sprite_stretched draw_sprite_stretched + +function draw_sprite_stretched_override(spr, ind, _x, _y, w = 1, h = 1) { + __draw_sprite_stretched(spr, ind, round(_x), round(_y), round(w), round(h)); +} + function draw_sprite_uniform(spr, ind, _x, _y, scale, color = c_white) { draw_sprite_ext(spr, ind, round(_x), round(_y), scale, scale, 0, color, 1); } diff --git a/scripts/panel_graph/panel_graph.gml b/scripts/panel_graph/panel_graph.gml index 1fccaaa8f..7d130c36a 100644 --- a/scripts/panel_graph/panel_graph.gml +++ b/scripts/panel_graph/panel_graph.gml @@ -125,6 +125,12 @@ function Panel_Graph() : PanelContent() constructor { initSize(); toolbars = [ + [ + THEME.icon_preview_export, + function() { return 0; }, + function() { return get_text("panel_graph_export_image", "Export graph as image"); }, + function() { dialogPanelCall(new Panel_Graph_Export_Image(self)); } + ], [ THEME.icon_center_canvas, function() { return 0; }, @@ -364,17 +370,15 @@ function Panel_Graph() : PanelContent() constructor { draw_set_alpha(grid_opacity * (graph_s >= 1? 1 : 0.5)); while(xx < w + gr_ls) { draw_line(xx + xs, 0, xx + xs, h); - if(xx + xs - gr_x == 0) { + if(xx + xs - gr_x == 0) draw_line_width(xx + xs, 0, xx + xs, h, 3); - } xx += gr_ls; } while(yy < h + gr_ls) { draw_line(0, yy + ys, w, yy + ys); - if(yy + ys - gr_y == 0) { + if(yy + ys - gr_y == 0) draw_line_width(0, yy + ys, w, yy + ys, 3); - } yy += gr_ls; } draw_set_alpha(1); @@ -1875,8 +1879,4 @@ function Panel_Graph() : PanelContent() constructor { ds_list_remove(nodes_list, node); ds_list_add(nodes_list, node); } - - static exportNodeImage = function() { - var dia = dialogPanelCall(new Panel_Graph_Export_Image(self)); - } } \ No newline at end of file diff --git a/scripts/panel_graph_export_image/panel_graph_export_image.gml b/scripts/panel_graph_export_image/panel_graph_export_image.gml index 07cbf5ba2..7dfba105e 100644 --- a/scripts/panel_graph_export_image/panel_graph_export_image.gml +++ b/scripts/panel_graph_export_image/panel_graph_export_image.gml @@ -1,13 +1,20 @@ -function graph_export_image(nodeList, settings = {}) { +function graph_export_image(allList, nodeList, settings = {}) { var amo = ds_list_size(nodeList); if(amo < 1) return; var scale = struct_try_get(settings, "scale", 1); var padding = struct_try_get(settings, "padding", 0); + var bgEnable = struct_try_get(settings, "bgEnable", false); var bgColor = struct_try_get(settings, "bgColor", c_black); + var gridEnable = struct_try_get(settings, "gridEnable", false); - var gridColor = struct_try_get(settings, "gridColor", c_black); + var gridColor = struct_try_get(settings, "gridColor", c_white); + var gridAlpha = struct_try_get(settings, "gridAlpha", 0); + + var borderPad = struct_try_get(settings, "borderPad", 0); + var borderColor = struct_try_get(settings, "borderColor", c_white); + var borderAlpha = struct_try_get(settings, "borderAlpha", 0.5); var bbox_x0 = nodeList[| 0].x * scale; var bbox_y0 = nodeList[| 0].y * scale; @@ -43,8 +50,33 @@ function graph_export_image(nodeList, settings = {}) { var gr_y = -bbox_y0; var mx = gr_x, my = gr_y; - for(var i = 0; i < ds_list_size(nodeList); i++) - nodeList[| i].preDraw(gr_x, gr_y, scale); + if(gridEnable) { + var gls = 32; + var gr_ls = gls * scale; + var xx = -gr_ls, xs = safe_mod(gr_x, gr_ls); + var yy = -gr_ls, ys = safe_mod(gr_y, gr_ls); + + draw_set_color(gridColor); + draw_set_alpha(gridAlpha); + while(xx < bbox_w + gr_ls) { + draw_line(xx + xs, 0, xx + xs, bbox_h); + if(xx + xs - gr_x == 0) + draw_line_width(xx + xs, 0, xx + xs, bbox_h, 3); + xx += gr_ls; + } + + while(yy < bbox_h + gr_ls) { + draw_line(0, yy + ys, bbox_w, yy + ys); + if(yy + ys - gr_y == 0) + draw_line_width(0, yy + ys, bbox_w, yy + ys, 3); + yy += gr_ls; + } + + draw_set_alpha(1); + } + + for(var i = 0; i < ds_list_size(allList); i++) + allList[| i].preDraw(gr_x, gr_y, scale); #region draw frame for(var i = 0; i < ds_list_size(nodeList); i++) { @@ -81,5 +113,25 @@ function graph_export_image(nodeList, settings = {}) { surface_reset_target(); - return s; + if(borderPad == 0) return s; + + var _sg = surface_create(bbox_w + borderPad * 2, bbox_h + borderPad * 2); + + surface_set_target(_sg); + if(bgEnable) draw_clear(bgColor); + else draw_clear_alpha(0, 0); + + draw_surface(s, borderPad, borderPad); + + draw_set_color(borderColor); + draw_set_alpha(borderAlpha); + + draw_rectangle(borderPad, borderPad, bbox_w + borderPad, bbox_h + borderPad, 1); + + draw_set_alpha(1); + surface_reset_target(); + + + surface_free(s); + return _sg; } \ No newline at end of file diff --git a/scripts/panel_graph_export_image_dialog/panel_graph_export_image_dialog.gml b/scripts/panel_graph_export_image_dialog/panel_graph_export_image_dialog.gml index 2e4863209..a2e1302b8 100644 --- a/scripts/panel_graph_export_image_dialog/panel_graph_export_image_dialog.gml +++ b/scripts/panel_graph_export_image_dialog/panel_graph_export_image_dialog.gml @@ -1,14 +1,99 @@ function Panel_Graph_Export_Image(targetPanel) : PanelContent() constructor { title = "Export Graph"; - w = ui(480); - h = ui(640); + w = ui(360); + h = ui(524); + min_h = h; self.targetPanel = targetPanel; + nodeList = targetPanel.nodes_list; surface = noone; - settings = {}; + settings = { + scale : 1, + padding : 64, + + bgEnable : false, + bgColor : COLORS.panel_bg_clear, + + gridEnable : false, + gridColor : c_white, + gridAlpha : 0.05, + + borderPad : 0, + borderColor : c_white, + borderAlpha : 0.05, + }; - nodeList = noone; + sel = 0; + nodes_select = [ "All nodes", "Selected" ]; + widgets = []; + widgets[0] = [ "Nodes", new scrollBox(nodes_select, function(val) { sel = val; nodeList = val? targetPanel.nodes_select_list : targetPanel.nodes_list; refresh(); }, false), + function() { return nodes_select[sel] } ]; + widgets[1] = [ "Scale", new textBox(TEXTBOX_INPUT.number, function(val) { settings.scale = val; refresh(); }), + function() { return settings.scale } ]; + widgets[2] = [ "Padding", new textBox(TEXTBOX_INPUT.number, function(val) { settings.padding = val; refresh(); }), + function() { return settings.padding } ]; + widgets[3] = [ "Solid Background", new checkBox(function() { settings.bgEnable = !settings.bgEnable; refresh(); }), + function() { return settings.bgEnable } ]; + widgets[4] = [ "Background Color", new buttonColor(function(val) { settings.bgColor = val; refresh(); }), + function() { return settings.bgColor } ]; + widgets[5] = [ "Render Grid", new checkBox(function() { settings.gridEnable = !settings.gridEnable; refresh(); }), + function() { return settings.gridEnable } ]; + widgets[6] = [ "Grid Color", new buttonColor(function(val) { settings.gridColor = val; refresh(); }), + function() { return settings.gridColor } ]; + widgets[7] = [ "Grid Opacity", new textBox(TEXTBOX_INPUT.number, function(val) { settings.gridAlpha = val; refresh(); }), + function() { return settings.gridAlpha } ]; + widgets[8] = [ "Border", new textBox(TEXTBOX_INPUT.number, function(val) { settings.borderPad = val; refresh(); }), + function() { return settings.borderPad } ]; + widgets[9] = [ "Border Color", new buttonColor(function(val) { settings.borderColor = val; refresh(); }), + function() { return settings.borderColor } ]; + widgets[10] = [ "Border Opacity", new textBox(TEXTBOX_INPUT.number, function(val) { settings.borderAlpha = val; refresh(); }), + function() { return settings.borderAlpha } ]; + + b_export = button(function() { + if(!is_surface(surface)) return; + + var path = get_save_filename("*.png", "Screenshot"); + if(path == -1) return; + + if(!filename_ext(path) != ".png") path += ".png"; + surface_save(surface, path); + noti_status($"Graph image exported at {path}"); + }); + + sc_settings = new scrollPane(w - ui(padding + padding), h - ui(title_height + padding + 204), function(_y, _m) { + draw_clear_alpha(COLORS.panel_bg_clear, 0); + + var _ww = ui(160); + var _hh = ui(30); + var _ss = ui(28); + var ty = _y + _hh / 2; + var _tx = sc_settings.surface_w; + var wh = ui(36); + + for( var i = 0; i < array_length(widgets); i++ ) { + draw_set_text(f_p1, fa_left, fa_center, COLORS._main_text); + draw_text_over(0, ty + wh * i, widgets[i][0]); + + var _wid = widgets[i][1]; + var _dat = widgets[i][2](); + _wid.setActiveFocus(pFOCUS, pHOVER); + + switch(instanceof(widgets[i][1])) { + case "textBox" : _wid.draw(_tx - _ww, ty + wh * i - _hh / 2, _ww, _hh, _dat, _m); break; + case "checkBox" : _wid.draw(_tx - _ww / 2 - _ss / 2, ty + wh * i - _ss / 2, _dat, _m); break; + case "buttonColor" : _wid.draw(_tx - _ww, ty + wh * i - _hh / 2, _ww, _hh, _dat, _m); break; + case "scrollBox" : _wid.draw(_tx - _ww, ty + wh * i - _hh / 2, _ww, _hh, _dat, _m, sc_settings.x + x, sc_settings.y + y); break; + } + } + + var _h = wh * array_length(widgets) + _hh; + return _h; + }) + + function onResize() { + sc_settings.resize(w - ui(padding + padding), h - ui(title_height + padding + 204)); + } function refresh() { if(is_surface(surface)) @@ -18,17 +103,52 @@ function Panel_Graph_Export_Image(targetPanel) : PanelContent() constructor { if(nodeList == noone) return; - surface = graph_export_image(nodeList, settings); - } + surface = graph_export_image(targetPanel.nodes_list, nodeList, settings); + } refresh(); function drawContent(panel) { - + var tx = padding; + var ty = padding; + var sh = 160; if(is_surface(surface)) { + var _sw = surface_get_width(surface); + var _sh = surface_get_height(surface); + var ss = min((w - padding * 2) / _sw, sh / _sh); + draw_surface_ext(surface, w / 2 - _sw * ss / 2, ty + sh / 2 - _sh * ss / 2, ss, ss, 0, c_white, 1); + + draw_set_text(f_p2, fa_center, fa_bottom, COLORS._main_text_sub); + draw_text_add(w / 2, ty + sh - ui(2), $"{surface_get_width(surface)} x {surface_get_height(surface)} px"); } - var tx = 0; - var ty = 0; + draw_set_color(COLORS._main_icon); + draw_rectangle(tx, ty, tx + w - padding * 2, ty + sh, 1); + + var bx = w - padding - ui(4) - ui(24); + var by = padding + ui(4); + var _m = [ mx, my ]; + + if(buttonInstant(THEME.button_hide, bx, by, ui(24), ui(24), _m, pFOCUS, pHOVER) == 2) + refresh(); + draw_sprite_ui(THEME.refresh_s, 0, bx + ui(12), by + ui(12),,,, COLORS._main_icon, 1); + + var sx = tx; + var sy = ty + sh + ui(16); + + sc_settings.setActiveFocus(pFOCUS, pHOVER); + sc_settings.draw(sx, sy, mx - sx, my - sy); + + if(is_surface(surface)) { + draw_set_text(f_p1, fa_left, fa_top, COLORS._main_text); + var _bw = string_width("Export") + ui(32); + var _bh = string_height("Export") + ui(12); + bx = w - padding - _bw; + by = h - padding - _bh; + + b_export.setActiveFocus(pFOCUS, pHOVER); + b_export.draw(bx, by, _bw, _bh, _m); + draw_text(bx + ui(16), by + ui(6), "Export"); + } } } \ No newline at end of file diff --git a/scripts/panel_preview/panel_preview.gml b/scripts/panel_preview/panel_preview.gml index d19741c95..7b410279d 100644 --- a/scripts/panel_preview/panel_preview.gml +++ b/scripts/panel_preview/panel_preview.gml @@ -128,16 +128,17 @@ function Panel_Preview() : PanelContent() constructor { ]; actions = [ - [ - THEME.icon_center_canvas, - get_text("panel_preview_center_canvas", "Center canvas"), - function() { fullView(); } - ], [ THEME.icon_preview_export, get_text("panel_preview_export_canvas", "Export canvas"), function() { saveCurrentFrame(); } ], + [ + THEME.icon_center_canvas, + get_text("panel_preview_center_canvas", "Center canvas"), + function() { fullView(); } + ], + ] tb_framerate = new textBox(TEXTBOX_INPUT.number, function(val) { preview_rate = real(val); });