palette alpha fix

This commit is contained in:
Tanasart 2024-04-20 10:00:28 +07:00
parent a768e60f21
commit dce6a37919
35 changed files with 6962 additions and 1918 deletions

View file

@ -0,0 +1,442 @@
// 2024-04-20 10:00:16
#event properties (no comments/etc. here are saved)
parent_index = _p_dialog_undo_block;
uses_physics = false;
#event create init
event_inherited();
#region data
dialog_w = ui(812);
dialog_h = ui(440);
title_height = 52;
destroy_on_click_out = true;
name = __txtx("palette_editor_title", "Palette editor");
palette = 0;
index_selecting = 0;
index_dragging = -1;
interactable = true;
index_drag_x = 0; index_drag_x_to = 0;
index_drag_y = 0; index_drag_y_to = 0;
index_drag_w = 0; index_drag_w_to = 0;
index_drag_h = 0; index_drag_h_to = 0;
setColor = function(color) {
if(index_selecting == -1 || palette == 0) return;
palette[index_selecting] = color;
if(onApply == noone) return;
onApply(palette);
}
onApply = noone;
selector = new colorSelector(setColor);
selector.dropper_close = false;
selector.discretize_pal = false;
previous_palette = c_black;
function setDefault(pal) {
setPalette(pal);
previous_palette = array_clone(pal);
}
b_cancel = button(function() {
onApply(previous_palette);
instance_destroy();
}).setIcon(THEME.undo, 0, COLORS._main_icon)
.setTooltip(__txtx("dialog_revert_and_exit", "Revert and exit"));
b_apply = button(function() {
onApply(palette);
instance_destroy();
}).setIcon(THEME.accept, 0, COLORS._main_icon_dark);
function setPalette(pal) {
palette = pal;
index_selecting = 0;
if(array_length(palette) > 0)
selector.setColor(palette[0]);
}
#endregion
#region presets
hovering_name = "";
sp_preset_w = ui(240 - 32 - 16);
sp_presets = new scrollPane(sp_preset_w, dialog_h - ui(62), function(_y, _m) {
var ww = sp_preset_w - ui(40);
var hh = ui(32);
var yy = _y + ui(8);
var hg = ui(52);
draw_clear_alpha(COLORS.panel_bg_clear, 0);
for(var i = -1; i < array_length(PALETTES); i++) {
var pal = i == -1? {
name: "project",
palette: PROJECT.attributes.palette,
path: ""
} : PALETTES[i];
var isHover = sHOVER && sp_presets.hover && point_in_rectangle(_m[0], _m[1], ui(4), yy, ui(4) + sp_preset_w - ui(16), yy + hg);
draw_sprite_stretched(THEME.ui_panel_bg, 3, ui(4), yy, sp_preset_w - ui(16), hg);
if(isHover)
draw_sprite_stretched_ext(THEME.node_active, 1, ui(4), yy, sp_preset_w - ui(16), hg, COLORS._main_accent, 1);
draw_set_text(f_p2, fa_left, fa_top, COLORS._main_text_sub);
draw_text(ui(16), yy + ui(8), pal.name);
drawPalette(pal.palette, ui(16), yy + ui(28), ww, ui(16));
if(isHover) {
if(mouse_press(mb_left, interactable && sFOCUS)) {
palette = array_clone(pal.palette);
onApply(palette);
index_selecting = 0;
selector.setColor(palette[index_selecting], false);
}
if(i >= 0 && mouse_press(mb_right, interactable && sFOCUS)) {
hovering = pal;
menuCall("palette_window_preset_menu",,, [
menuItem(__txtx("palette_editor_set_default", "Set as default"), function() {
DEF_PALETTE = array_clone(hovering.palette);
}),
menuItem(__txtx("palette_editor_delete", "Delete palette"), function() {
file_delete(hovering.path);
__initPalette();
}),
]);
}
}
yy += hg + ui(4);
hh += hg + ui(4);
}
return hh;
});
sp_presets.always_scroll = true;
#endregion
#region tools
function sortPalette(sortFunc) {
array_sort(palette, sortFunc);
onApply(palette);
}
#endregion
#region action
onResize = function() {
sp_presets.resize(sp_preset_w, dialog_h - ui(62));
}
function checkMouse() {}
#endregion
#event step_begin init
if !ready exit;
#region destroy
selector.interactable = interactable;
if(!selector.dropper_active) {
if(sHOVER && !point_in_rectangle(mouse_mx, mouse_my, dialog_x, dialog_y, dialog_x + dialog_w, dialog_y + dialog_h)) {
if(destroy_on_click_out && mouse_press(mb_left))
instance_destroy(self);
}
doDrag();
}
if(sFOCUS && WIDGET_CURRENT == noone) {
if(keyboard_check_pressed(vk_enter)) {
onApply(palette);
instance_destroy();
}
if(keyboard_check_pressed(vk_escape)) {
onApply(previous_palette);
instance_destroy();
}
}
#endregion
#region resize
if(_dialog_h != dialog_h || _dialog_w != dialog_w) {
_dialog_h = dialog_h;
_dialog_w = dialog_w;
if(onResize != -1) onResize();
}
#endregion
#event draw_gui init
if !ready exit;
if palette == 0 exit;
#region dropper
selector.interactable = interactable;
if(selector.dropper_active) {
selector.drawDropper(self);
exit;
}
#endregion
#region base UI
var presets_x = dialog_x;
var presets_w = ui(240);
var content_x = dialog_x + presets_w + ui(16);
var content_w = dialog_w - presets_w - ui(16);
var p = DIALOG_PAD;
var p2 = DIALOG_PAD * 2;
draw_sprite_stretched(THEME.dialog_bg, 0, presets_x - p, dialog_y - p, presets_w + p2, dialog_h + p2);
if(sFOCUS) draw_sprite_stretched_ext(THEME.dialog_active, 0, presets_x - p, dialog_y - p, presets_w + p2, dialog_h + p2, COLORS._main_accent, 1);
draw_sprite_stretched(THEME.dialog_bg, 0, content_x - p, dialog_y - p, content_w + p2, dialog_h + p2);
if(sFOCUS) draw_sprite_stretched_ext(THEME.dialog_active, 0, content_x - p, dialog_y - p, content_w + p2, dialog_h + p2, COLORS._main_accent, 1);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text(presets_x + ui(24), dialog_y + ui(16), __txt("Presets"));
draw_text(content_x + (!interactable * ui(32)) + ui(24), dialog_y + ui(16), name);
if(!interactable)
draw_sprite_ui(THEME.lock, 0, content_x + ui(24 + 12), dialog_y + ui(16 + 12),,,, COLORS._main_icon);
#endregion
#region presets
draw_sprite_stretched(THEME.ui_panel_bg, 1, presets_x + ui(16), dialog_y + ui(44), ui(240 - 32), dialog_h - ui(60));
sp_presets.setFocusHover(sFOCUS, sHOVER);
sp_presets.draw(presets_x + ui(24), dialog_y + ui(44));
var bx = presets_x + presets_w - ui(44);
var by = dialog_y + ui(12);
var bs = ui(28);
if(buttonInstant(THEME.button_hide, bx, by, bs, bs, mouse_ui, sFOCUS, sHOVER, __txtx("add_preset", "Add to preset"), THEME.add_20) == 2) {
var dia = dialogCall(o_dialog_file_name, mouse_mx + ui(8), mouse_my + ui(8));
dia.onModify = function (txt) {
var file = file_text_open_write(txt + ".hex");
for(var i = 0; i < array_length(palette); i++) {
var cc = palette[i];
var r = number_to_hex(color_get_red(cc));
var g = number_to_hex(color_get_green(cc));
var b = number_to_hex(color_get_blue(cc));
var a = number_to_hex(color_get_alpha(cc));
file_text_write_string(file, $"{r}{g}{b}{a}\n");
}
file_text_close(file);
__initPalette();
};
dia.path = DIRECTORY + "Palettes/"
}
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, bs, bs, mouse_ui, sFOCUS, sHOVER, __txt("Refresh"), THEME.refresh_20) == 2)
__initPalette();
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, bs, bs, mouse_ui, sFOCUS, sHOVER, __txtx("color_selector_open_palette", "Open palette folder"), THEME.path_open_20) == 2) {
var _realpath = DIRECTORY + "Palettes";
shellOpenExplorer(_realpath)
}
draw_sprite_ui_uniform(THEME.path_open_20, 1, bx + bs / 2, by + bs / 2, 1, c_white);
bx -= ui(32);
#endregion
#region palette
var pl_x = content_x + ui(60);
var pl_y = dialog_y + ui(54);
var pl_w = content_w - ui(154);
var pl_h = ui(24);
var max_col = 8;
var col = min(array_length(palette), max_col);
var row = ceil(array_length(palette) / col);
var ww = round(pl_w / col);
var hh = (pl_h + ui(6)) * row;
dialog_h = ui(408) + hh;
draw_sprite_stretched(THEME.textbox, 3, pl_x - ui(6), pl_y - ui(6), pl_w + ui(12), hh + ui(6));
draw_sprite_stretched(THEME.textbox, 0, pl_x - ui(6), pl_y - ui(6), pl_w + ui(12), hh + ui(6));
#region tools
var bx = content_x + content_w - ui(50);
var by = dialog_y + ui(16);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, __txtx("palette_editor_sort", "Sort color"), THEME.sort) == 2) {
menuCall("palette_window_sort_menu", bx + ui(32), by, [
menuItem(__txtx("palette_editor_sort_brighter", "Brighter"), function() { sortPalette(__sortBright); }),
menuItem(__txtx("palette_editor_sort_darker", "Darker"), function() { sortPalette(__sortDark); }),
-1,
menuItem(__txtx("palette_editor_sort_hue", "Hue"), function() { sortPalette(__sortHue); }),
menuItem(__txtx("palette_editor_sort_sat", "Saturation"), function() { sortPalette(__sortSat); }),
menuItem(__txtx("palette_editor_sort_val", "Value"), function() { sortPalette(__sortVal); }),
],, palette);
}
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, __txtx("palette_editor_reverse", "Reverse palette"), THEME.reverse) == 2) {
palette = array_reverse(palette);
onApply(palette);
}
bx -= ui(32);
#endregion
var hover = -1, hvx, hvy;
var _pd = ui(5);
for(var i = 0; i < row; i++)
for(var j = 0; j < col; j++) {
var index = i * col + j;
if(index >= array_length(palette)) break;
var _p = palette[index];
var _pa = _color_get_alpha(_p);
var _kx = pl_x + j * ww;
var _ky = pl_y + i * (pl_h + ui(6));
var _px = _kx + ui(2);
var _py = _ky;
var _pw = ww - ui(4);
var _ph = pl_h;
if(index == index_dragging) {
index_drag_x_to = _px;
index_drag_y_to = _py;
index_drag_w_to = _pw;
index_drag_h_to = _ph;
continue;
}
if(_pa < 1) {
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph - ui(8), _p, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw, ui(6), c_black, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw * _pa, ui(6), c_white, 1);
} else
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph, _p, 1);
if(index == index_selecting)
draw_sprite_stretched_ext(THEME.palette_selecting, 0, _px - _pd, _py - _pd, _pw + _pd * 2, _ph + _pd * 2, c_white, 1);
if(sHOVER && point_in_rectangle(mouse_mx, mouse_my, _kx, _ky, _kx + ww, _ky + pl_h)) {
hover = index;
hvx = _kx;
hvy = _ky;
}
}
if(index_dragging > -1) {
index_drag_x = index_drag_x == 0? index_drag_x_to : lerp_float(index_drag_x, index_drag_x_to, 5);
index_drag_y = index_drag_y == 0? index_drag_y_to : lerp_float(index_drag_y, index_drag_y_to, 5);
index_drag_w = index_drag_w == 0? index_drag_w_to : lerp_float(index_drag_w, index_drag_w_to, 5);
index_drag_h = index_drag_h == 0? index_drag_h_to : lerp_float(index_drag_h, index_drag_h_to, 5);
_px = index_drag_x;
_py = index_drag_y;
_pw = index_drag_w;
_ph = index_drag_h;
_p = palette[index_dragging];
_pa = _color_get_alpha(_p);
if(_pa < 1) {
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph - ui(8), _p, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw, ui(6), c_black, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw * _pa, ui(6), c_white, 1);
} else
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph, _p, 1);
draw_sprite_stretched_ext(THEME.palette_selecting, 0, _px - _pd, _py - _pd, _pw + _pd * 2, _ph + _pd * 2, c_white, 1);
if(hover > -1 && hover != index_dragging) {
draw_set_color(COLORS.dialog_palette_divider);
var sx = hvx;
if(hover >= index_dragging) sx += ww;
var tt = palette[index_dragging];
array_delete(palette, index_dragging, 1);
array_insert(palette, hover, tt);
index_selecting = hover;
index_dragging = hover;
onApply(palette);
}
if(mouse_release(mb_left))
index_dragging = -1;
} else {
index_drag_x = 0;
index_drag_y = 0;
index_drag_w = 0;
index_drag_h = 0;
}
if(mouse_press(mb_left, sFOCUS) && hover > -1) {
index_selecting = hover;
if(interactable)
index_dragging = hover;
selector.setColor(palette[hover]);
}
var bx = content_x + content_w - ui(50);
var by = pl_y - ui(2);
if(array_length(palette) > 1) {
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, "", THEME.minus) == 2) {
array_delete(palette, index_selecting, 1);
index_selecting = clamp(index_selecting - 1, 0, array_length(palette) - 1);
onApply(palette);
}
} else {
draw_sprite_ui_uniform(THEME.minus, 0, bx + ui(14), by + ui(14), 1, COLORS._main_icon, 0.5);
}
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, "", THEME.add) == 2) {
index_selecting = array_length(palette);
palette[array_length(palette)] = c_black;
onApply(palette);
}
bx = content_x + ui(18);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, __txtx("palette_editor_load", "Load palette file") + " (.hex)", THEME.file) == 2) {
var path = get_open_filename("HEX palette|*.hex", "");
key_release();
if(isPaletteFile(path)) {
palette = loadPalette(path);
onApply(palette);
}
}
draw_sprite_ui_uniform(THEME.file, 0, bx + ui(14), by + ui(14), 1, COLORS._main_icon);
#endregion
#region selector
var col_x = content_x + ui(20);
var col_y = dialog_y + ui(70) + hh;
selector.draw(col_x, col_y, sFOCUS, sHOVER);
#endregion
#region controls
var bx = content_x + content_w - ui(36);
var by = dialog_y + dialog_h - ui(36);
b_apply.register();
b_apply.setFocusHover(sFOCUS, sHOVER);
b_apply.draw(bx - ui(18), by - ui(18), ui(36), ui(36), mouse_ui, THEME.button_lime);
bx -= ui(48);
b_cancel.register();
b_cancel.setFocusHover(sFOCUS, sHOVER);
b_cancel.draw(bx - ui(18), by - ui(18), ui(36), ui(36), mouse_ui, THEME.button_hide);
#endregion
#event draw_gui_end
selector.colorPicker();

View file

@ -0,0 +1,442 @@
// 2024-04-20 09:59:48
#event properties (no comments/etc. here are saved)
parent_index = _p_dialog_undo_block;
uses_physics = false;
#event create init
event_inherited();
#region data
dialog_w = ui(812);
dialog_h = ui(440);
title_height = 52;
destroy_on_click_out = true;
name = __txtx("palette_editor_title", "Palette editor");
palette = 0;
index_selecting = 0;
index_dragging = -1;
interactable = true;
index_drag_x = 0; index_drag_x_to = 0;
index_drag_y = 0; index_drag_y_to = 0;
index_drag_w = 0; index_drag_w_to = 0;
index_drag_h = 0; index_drag_h_to = 0;
setColor = function(color) {
if(index_selecting == -1 || palette == 0) return;
palette[index_selecting] = color;
if(onApply == noone) return;
onApply(palette);
}
onApply = noone;
selector = new colorSelector(setColor);
selector.dropper_close = false;
selector.discretize_pal = false;
previous_palette = c_black;
function setDefault(pal) {
setPalette(pal);
previous_palette = array_clone(pal);
}
b_cancel = button(function() {
onApply(previous_palette);
instance_destroy();
}).setIcon(THEME.undo, 0, COLORS._main_icon)
.setTooltip(__txtx("dialog_revert_and_exit", "Revert and exit"));
b_apply = button(function() {
onApply(palette);
instance_destroy();
}).setIcon(THEME.accept, 0, COLORS._main_icon_dark);
function setPalette(pal) {
palette = pal;
index_selecting = 0;
if(array_length(palette) > 0)
selector.setColor(palette[0]);
}
#endregion
#region presets
hovering_name = "";
sp_preset_w = ui(240 - 32 - 16);
sp_presets = new scrollPane(sp_preset_w, dialog_h - ui(62), function(_y, _m) {
var ww = sp_preset_w - ui(40);
var hh = ui(32);
var yy = _y + ui(8);
var hg = ui(52);
draw_clear_alpha(COLORS.panel_bg_clear, 0);
for(var i = -1; i < array_length(PALETTES); i++) {
var pal = i == -1? {
name: "project",
palette: PROJECT.attributes.palette,
path: ""
} : PALETTES[i];
var isHover = sHOVER && sp_presets.hover && point_in_rectangle(_m[0], _m[1], ui(4), yy, ui(4) + sp_preset_w - ui(16), yy + hg);
draw_sprite_stretched(THEME.ui_panel_bg, 3, ui(4), yy, sp_preset_w - ui(16), hg);
if(isHover)
draw_sprite_stretched_ext(THEME.node_active, 1, ui(4), yy, sp_preset_w - ui(16), hg, COLORS._main_accent, 1);
draw_set_text(f_p2, fa_left, fa_top, COLORS._main_text_sub);
draw_text(ui(16), yy + ui(8), pal.name);
drawPalette(pal.palette, ui(16), yy + ui(28), ww, ui(16));
if(isHover) {
if(mouse_press(mb_left, interactable && sFOCUS)) {
palette = array_clone(pal.palette);
onApply(palette);
index_selecting = 0;
selector.setColor(palette[index_selecting], false);
}
if(i >= 0 && mouse_press(mb_right, interactable && sFOCUS)) {
hovering = pal;
menuCall("palette_window_preset_menu",,, [
menuItem(__txtx("palette_editor_set_default", "Set as default"), function() {
DEF_PALETTE = array_clone(hovering.palette);
}),
menuItem(__txtx("palette_editor_delete", "Delete palette"), function() {
file_delete(hovering.path);
__initPalette();
}),
]);
}
}
yy += hg + ui(4);
hh += hg + ui(4);
}
return hh;
});
sp_presets.always_scroll = true;
#endregion
#region tools
function sortPalette(sortFunc) {
array_sort(palette, sortFunc);
onApply(palette);
}
#endregion
#region action
onResize = function() {
sp_presets.resize(sp_preset_w, dialog_h - ui(62));
}
function checkMouse() {}
#endregion
#event step_begin init
if !ready exit;
#region destroy
selector.interactable = interactable;
if(!selector.dropper_active) {
if(sHOVER && !point_in_rectangle(mouse_mx, mouse_my, dialog_x, dialog_y, dialog_x + dialog_w, dialog_y + dialog_h)) {
if(destroy_on_click_out && mouse_press(mb_left))
instance_destroy(self);
}
doDrag();
}
if(sFOCUS && WIDGET_CURRENT == noone) {
if(keyboard_check_pressed(vk_enter)) {
onApply(palette);
instance_destroy();
}
if(keyboard_check_pressed(vk_escape)) {
onApply(previous_palette);
instance_destroy();
}
}
#endregion
#region resize
if(_dialog_h != dialog_h || _dialog_w != dialog_w) {
_dialog_h = dialog_h;
_dialog_w = dialog_w;
if(onResize != -1) onResize();
}
#endregion
#event draw_gui init
if !ready exit;
if palette == 0 exit;
#region dropper
selector.interactable = interactable;
if(selector.dropper_active) {
selector.drawDropper(self);
exit;
}
#endregion
#region base UI
var presets_x = dialog_x;
var presets_w = ui(240);
var content_x = dialog_x + presets_w + ui(16);
var content_w = dialog_w - presets_w - ui(16);
var p = DIALOG_PAD;
var p2 = DIALOG_PAD * 2;
draw_sprite_stretched(THEME.dialog_bg, 0, presets_x - p, dialog_y - p, presets_w + p2, dialog_h + p2);
if(sFOCUS) draw_sprite_stretched_ext(THEME.dialog_active, 0, presets_x - p, dialog_y - p, presets_w + p2, dialog_h + p2, COLORS._main_accent, 1);
draw_sprite_stretched(THEME.dialog_bg, 0, content_x - p, dialog_y - p, content_w + p2, dialog_h + p2);
if(sFOCUS) draw_sprite_stretched_ext(THEME.dialog_active, 0, content_x - p, dialog_y - p, content_w + p2, dialog_h + p2, COLORS._main_accent, 1);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text(presets_x + ui(24), dialog_y + ui(16), __txt("Presets"));
draw_text(content_x + (!interactable * ui(32)) + ui(24), dialog_y + ui(16), name);
if(!interactable)
draw_sprite_ui(THEME.lock, 0, content_x + ui(24 + 12), dialog_y + ui(16 + 12),,,, COLORS._main_icon);
#endregion
#region presets
draw_sprite_stretched(THEME.ui_panel_bg, 1, presets_x + ui(16), dialog_y + ui(44), ui(240 - 32), dialog_h - ui(60));
sp_presets.setFocusHover(sFOCUS, sHOVER);
sp_presets.draw(presets_x + ui(24), dialog_y + ui(44));
var bx = presets_x + presets_w - ui(44);
var by = dialog_y + ui(12);
var bs = ui(28);
if(buttonInstant(THEME.button_hide, bx, by, bs, bs, mouse_ui, sFOCUS, sHOVER, __txtx("add_preset", "Add to preset"), THEME.add_20) == 2) {
var dia = dialogCall(o_dialog_file_name, mouse_mx + ui(8), mouse_my + ui(8));
dia.onModify = function (txt) {
var file = file_text_open_write(txt + ".hex");
for(var i = 0; i < array_length(palette); i++) {
var cc = palette[i];
var r = number_to_hex(color_get_red(cc));
var g = number_to_hex(color_get_green(cc));
var b = number_to_hex(color_get_blue(cc));
var a = number_to_hex(color_get_alpha(cc));
file_text_write_string(file, $"{r}{g}{b}{a}\n");
}
file_text_close(file);
__initPalette();
};
dia.path = DIRECTORY + "Palettes/"
}
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, bs, bs, mouse_ui, sFOCUS, sHOVER, __txt("Refresh"), THEME.refresh_20) == 2)
__initPalette();
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, bs, bs, mouse_ui, sFOCUS, sHOVER, __txtx("color_selector_open_palette", "Open palette folder"), THEME.path_open_20) == 2) {
var _realpath = DIRECTORY + "Palettes";
shellOpenExplorer(_realpath)
}
draw_sprite_ui_uniform(THEME.path_open_20, 1, bx + bs / 2, by + bs / 2, 1, c_white);
bx -= ui(32);
#endregion
#region palette
var pl_x = content_x + ui(60);
var pl_y = dialog_y + ui(54);
var pl_w = content_w - ui(154);
var pl_h = ui(24);
var max_col = 8;
var col = min(array_length(palette), max_col);
var row = ceil(array_length(palette) / col);
var ww = round(pl_w / col);
var hh = (pl_h + ui(6)) * row;
dialog_h = ui(408) + hh;
draw_sprite_stretched(THEME.textbox, 3, pl_x - ui(6), pl_y - ui(6), pl_w + ui(12), hh + ui(6));
draw_sprite_stretched(THEME.textbox, 0, pl_x - ui(6), pl_y - ui(6), pl_w + ui(12), hh + ui(6));
#region tools
var bx = content_x + content_w - ui(50);
var by = dialog_y + ui(16);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, __txtx("palette_editor_sort", "Sort color"), THEME.sort) == 2) {
menuCall("palette_window_sort_menu", bx + ui(32), by, [
menuItem(__txtx("palette_editor_sort_brighter", "Brighter"), function() { sortPalette(__sortBright); }),
menuItem(__txtx("palette_editor_sort_darker", "Darker"), function() { sortPalette(__sortDark); }),
-1,
menuItem(__txtx("palette_editor_sort_hue", "Hue"), function() { sortPalette(__sortHue); }),
menuItem(__txtx("palette_editor_sort_sat", "Saturation"), function() { sortPalette(__sortSat); }),
menuItem(__txtx("palette_editor_sort_val", "Value"), function() { sortPalette(__sortVal); }),
],, palette);
}
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, __txtx("palette_editor_reverse", "Reverse palette"), THEME.reverse) == 2) {
palette = array_reverse(palette);
onApply(palette);
}
bx -= ui(32);
#endregion
var hover = -1, hvx, hvy;
var _pd = ui(5);
for(var i = 0; i < row; i++)
for(var j = 0; j < col; j++) {
var index = i * col + j;
if(index >= array_length(palette)) break;
var _p = palette[index];
var _pa = _color_get_alpha(_p);
var _kx = pl_x + j * ww;
var _ky = pl_y + i * (pl_h + ui(6));
var _px = _kx + ui(2);
var _py = _ky;
var _pw = ww - ui(4);
var _ph = pl_h;
if(index == index_dragging) {
index_drag_x_to = _px;
index_drag_y_to = _py;
index_drag_w_to = _pw;
index_drag_h_to = _ph;
continue;
}
if(_pa < 1) {
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph - ui(8), _p, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw, ui(6), c_black, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw * _pa, ui(6), c_white, 1);
} else
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph, _p, 1);
if(index == index_selecting)
draw_sprite_stretched_ext(THEME.palette_selecting, 0, _px - _pd, _py - _pd, _pw + _pd * 2, _ph + _pd * 2, c_white, 1);
if(sHOVER && point_in_rectangle(mouse_mx, mouse_my, _kx, _ky, _kx + ww, _ky + pl_h)) {
hover = index;
hvx = _kx;
hvy = _ky;
}
}
if(index_dragging > -1) {
index_drag_x = index_drag_x == 0? index_drag_x_to : lerp_float(index_drag_x, index_drag_x_to, 5);
index_drag_y = index_drag_y == 0? index_drag_y_to : lerp_float(index_drag_y, index_drag_y_to, 5);
index_drag_w = index_drag_w == 0? index_drag_w_to : lerp_float(index_drag_w, index_drag_w_to, 5);
index_drag_h = index_drag_h == 0? index_drag_h_to : lerp_float(index_drag_h, index_drag_h_to, 5);
_px = index_drag_x;
_py = index_drag_y;
_pw = index_drag_w;
_ph = index_drag_h;
_p = palette[index_dragging];
_pa = _color_get_alpha(_p);
if(_pa < 1) {
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph - ui(8), _p, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw, ui(6), c_black, 1);
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py + _ph - ui(6), _pw * _pa, ui(6), c_white, 1);
} else
draw_sprite_stretched_ext(THEME.palette_mask, 1, _px, _py, _pw, _ph, _p, 1);
draw_sprite_stretched_ext(THEME.palette_selecting, 0, _px - _pd, _py - _pd, _pw + _pd * 2, _ph + _pd * 2, c_white, 1);
if(hover > -1 && hover != index_dragging) {
draw_set_color(COLORS.dialog_palette_divider);
var sx = hvx;
if(hover >= index_dragging) sx += ww;
var tt = palette[index_dragging];
array_delete(palette, index_dragging, 1);
array_insert(palette, hover, tt);
index_selecting = hover;
index_dragging = hover;
onApply(palette);
}
if(mouse_release(mb_left))
index_dragging = -1;
} else {
index_drag_x = 0;
index_drag_y = 0;
index_drag_w = 0;
index_drag_h = 0;
}
if(mouse_press(mb_left, sFOCUS) && hover > -1) {
index_selecting = hover;
if(interactable)
index_dragging = hover;
selector.setColor(palette[hover]);
}
var bx = content_x + content_w - ui(50);
var by = pl_y - ui(2);
if(array_length(palette) > 1) {
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, "", THEME.minus) == 2) {
array_delete(palette, index_selecting, 1);
index_selecting = clamp(index_selecting - 1, 0, array_length(palette) - 1);
onApply(palette);
}
} else {
draw_sprite_ui_uniform(THEME.minus, 0, bx + ui(14), by + ui(14), 1, COLORS._main_icon, 0.5);
}
bx -= ui(32);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, "", THEME.add) == 2) {
index_selecting = array_length(palette);
palette[array_length(palette)] = c_black;
onApply(palette);
}
bx = content_x + ui(18);
if(buttonInstant(THEME.button_hide, bx, by, ui(28), ui(28), mouse_ui, interactable && sFOCUS, sHOVER, __txtx("palette_editor_load", "Load palette file") + " (.hex)", THEME.file) == 2) {
var path = get_open_filename("HEX palette|*.hex", "");
key_release();
if(isPaletteFile(path)) {
palette = loadPalette(path);
onApply(palette);
}
}
draw_sprite_ui_uniform(THEME.file, 0, bx + ui(14), by + ui(14), 1, COLORS._main_icon);
#endregion
#region selector
var col_x = content_x + ui(20);
var col_y = dialog_y + ui(70) + hh;
selector.draw(col_x, col_y, sFOCUS, sHOVER);
#endregion
#region controls
var bx = content_x + content_w - ui(36);
var by = dialog_y + dialog_h - ui(36);
b_apply.register();
b_apply.setFocusHover(sFOCUS, sHOVER);
b_apply.draw(bx - ui(18), by - ui(18), ui(36), ui(36), mouse_ui, THEME.button_lime);
bx -= ui(48);
b_cancel.register();
b_cancel.setFocusHover(sFOCUS, sHOVER);
b_cancel.draw(bx - ui(18), by - ui(18), ui(36), ui(36), mouse_ui, THEME.button_hide);
#endregion
#event draw_gui_end
selector.colorPicker();

View file

@ -1,4 +1,4 @@
// 2024-04-19 07:55:21
// 2024-04-20 08:26:31
function Node_Canvas(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
name = "Canvas";
color = COLORS.node_blend_canvas;

View file

@ -1,4 +1,4 @@
// 2024-04-18 18:31:59
// 2024-04-20 08:25:39
function Node_Canvas(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
name = "Canvas";
color = COLORS.node_blend_canvas;

View file

@ -0,0 +1,305 @@
// 2024-04-20 08:21:26
#macro DEF_SURF_W PROJECT.attributes.surface_dimension[0]
#macro DEF_SURF_H PROJECT.attributes.surface_dimension[1]
#macro DEF_SURF PROJECT.attributes.surface_dimension
#macro DEF_PALETTE PROJECT.attributes.palette
#region
function node_draw_transform_init() {
drag_type = -1;
dragging_sx = 0;
dragging_sy = 0;
dragging_mx = 0;
dragging_my = 0;
rot_anc_x = 0;
rot_anc_y = 0;
}
function node_draw_transform_box(active, _x, _y, _s, _mx, _my, _snx, _sny, _posInd, _rotInd, _scaInd, _scaUnit = false) {
var _pos = getInputData(_posInd);
var _rot = getInputData(_rotInd);
var _sca = getInputData(_scaInd);
if(drag_type > -1) {
if(drag_type == 0) {
var _dx = (_mx - dragging_mx) / _s;
var _dy = (_my - dragging_my) / _s;
if(key_mod_press(SHIFT)) {
if(abs(_dx) > abs(_dy) + ui(16))
_dy = 0;
else if(abs(_dy) > abs(_dx) + ui(16))
_dx = 0;
else {
_dx = max(_dx, _dy);
_dy = _dx;
}
}
_pos[0] = value_snap(dragging_sx + _dx, _snx);
_pos[1] = value_snap(dragging_sy + _dy, _sny);
if(inputs[| _posInd].setValue(_pos))
UNDO_HOLDING = true;
if(inputs[| _posInd].unit.mode == VALUE_UNIT.reference) {
var p = [ _pos[0], _pos[1] ];
_pos = inputs[| _posInd].unit.apply(p);
}
} else if(drag_type == 1) {
var aa = point_direction(rot_anc_x, rot_anc_y, _mx, _my);
var da = angle_difference(dragging_mx, aa);
if(key_mod_press(CTRL))
_rot = round((dragging_sx - da) / 15) * 15;
else
_rot = dragging_sx - da;
if(inputs[| _rotInd].setValue(_rot))
UNDO_HOLDING = true;
} else if(drag_type == 2) {
var _p = point_rotate(_mx - dragging_mx, _my - dragging_my, 0, 0, -_rot);
_sca[0] = _p[0] / _s;
_sca[1] = _p[1] / _s;
if(key_mod_press(SHIFT)) {
_sca[0] = min(_sca[0], _sca[1]);
_sca[1] = min(_sca[0], _sca[1]);
}
if(inputs[| _scaInd].setValue(_sca))
UNDO_HOLDING = true;
if(_scaUnit && inputs[| _scaInd].unit.mode == VALUE_UNIT.reference) {
var s = [ _sca[0], _sca[1] ];
_sca = inputs[| _scaInd].unit.apply(s);
}
}
if(mouse_release(mb_left)) {
drag_type = -1;
UNDO_HOLDING = false;
}
}
var p0 = point_rotate(-_sca[0], -_sca[1], 0, 0, _rot);
var p1 = point_rotate( _sca[0], -_sca[1], 0, 0, _rot);
var p2 = point_rotate(-_sca[0], _sca[1], 0, 0, _rot);
var p3 = point_rotate( _sca[0], _sca[1], 0, 0, _rot);
var pr = point_rotate( 0, -_sca[1] - 1, 0, 0, _rot);
var pd0x = _x + (p0[0] + _pos[0]) * _s; var pd0y = _y + (p0[1] + _pos[1]) * _s;
var pd1x = _x + (p1[0] + _pos[0]) * _s; var pd1y = _y + (p1[1] + _pos[1]) * _s;
var pd2x = _x + (p2[0] + _pos[0]) * _s; var pd2y = _y + (p2[1] + _pos[1]) * _s;
var pd3x = _x + (p3[0] + _pos[0]) * _s; var pd3y = _y + (p3[1] + _pos[1]) * _s;
var prx = _x + (pr[0] + _pos[0]) * _s; var pry = _y + (pr[1] + _pos[1]) * _s;
var hovering = -1;
if(drag_type == -1) {
if(point_in_rectangle_points(_mx, _my, pd0x, pd0y, pd1x, pd1y, pd2x, pd2y, pd3x, pd3y))
hovering = 0;
if(point_in_circle(_mx, _my, prx, pry, 12))
hovering = 1;
if(point_in_circle(_mx, _my, pd3x, pd3y, 12))
hovering = 2;
}
draw_set_color(COLORS._main_accent);
draw_line_width(pd0x, pd0y, pd1x, pd1y, hovering == 0? 2 : 1);
draw_line_width(pd0x, pd0y, pd2x, pd2y, hovering == 0? 2 : 1);
draw_line_width(pd3x, pd3y, pd1x, pd1y, hovering == 0? 2 : 1);
draw_line_width(pd3x, pd3y, pd2x, pd2y, hovering == 0? 2 : 1);
draw_sprite_colored(THEME.anchor_rotate, hovering == 1, prx, pry,, _rot);
draw_sprite_colored(THEME.anchor_scale, hovering == 2, pd3x, pd3y,, _rot);
if(hovering == -1) return;
if(drag_type > -1) return;
if(mouse_press(mb_left, active)) {
drag_type = hovering;
if(hovering == 0) {
dragging_sx = _pos[0];
dragging_sy = _pos[1];
dragging_mx = _mx;
dragging_my = _my;
} else if(hovering == 1) { //rot
dragging_sx = _rot;
rot_anc_x = _x + _pos[0] * _s;
rot_anc_y = _y + _pos[1] * _s;
dragging_mx = point_direction(rot_anc_x, rot_anc_y, _mx, _my);
} else if(hovering == 2) { //sca
dragging_sx = _sca[0];
dragging_sy = _sca[1];
dragging_mx = _x + _pos[0] * _s;
dragging_my = _y + _pos[1] * _s;
}
}
}
#endregion
#region node function
function nodeLoad(_data, scale = false, _group = noone) {
INLINE
if(!is_struct(_data)) return;
var _x = _data.x;
var _y = _data.y;
var _type = _data.type;
if(ds_map_exists(APPEND_MAP, _data.id)) {
var _node = APPEND_MAP[? _data.id];
_node.x = _x;
_node.y = _y;
_node.deserialize(_data, scale);
return _node;
}
var _node = nodeBuild(_type, _x, _y, _group);
if(_node) _node.deserialize(_data, scale);
return _node;
}
function nodeCleanUp() {
var key = ds_map_find_first(PROJECT.nodeMap);
repeat(ds_map_size(PROJECT.nodeMap)) {
if(PROJECT.nodeMap[? key]) {
PROJECT.nodeMap[? key].active = false;
PROJECT.nodeMap[? key].cleanUp();
delete PROJECT.nodeMap[? key];
}
key = ds_map_find_next(PROJECT.nodeMap, key);
}
ds_map_clear(APPEND_MAP);
}
function graphFocusNode(node) {
PANEL_INSPECTOR.setInspecting(node);
PANEL_GRAPH.nodes_selecting = [ node ];
PANEL_GRAPH.fullView();
}
function refreshNodeMap() {
ds_map_clear(PROJECT.nodeNameMap);
var key = ds_map_find_first(PROJECT.nodeMap);
var amo = ds_map_size(PROJECT.nodeMap);
repeat(amo) {
var node = PROJECT.nodeMap[? key];
if(node.internalName != "")
PROJECT.nodeNameMap[? node.internalName] = node;
key = ds_map_find_next(PROJECT.nodeMap, key);
}
}
function nodeGetData(str) {
str = string_trim(str);
var strs = string_splice(str, ".");
if(array_length(strs) == 0) return 0;
if(array_length(strs) == 1) {
var splt = string_splice(strs[0], "[");
var inp = PROJECT.globalNode.getInput(strs[0]);
if(inp == 0) return 0;
var _arr = [ 0, 0 ];
inp.getValueRecursive(_arr);
return _arr[0];
} else if(struct_has(PROJECT_VARIABLES, strs[0])) {
var _str_var = PROJECT_VARIABLES[$ strs[0]];
if(!struct_has(_str_var, strs[1])) return 0;
var val = _str_var[$ strs[1]][0];
if(is_callable(val))
return val();
return val;
} else if(array_length(strs) > 2) {
var key = strs[0];
if(!ds_map_exists(PROJECT.nodeNameMap, key)) return 0;
var node = PROJECT.nodeNameMap[? key];
var map = noone;
switch(string_lower(strs[1])) {
case "inputs" :
case "input" :
map = node.inputMap;
break;
case "outputs" :
case "output" :
map = node.outputMap;
break;
default : return 0;
}
var _junc_key = string_lower(strs[2]);
var _junc = ds_map_try_get(map, _junc_key, noone);
if(_junc == noone) return 0;
return _junc.getValue();
}
return 0;
}
function nodeGetDataAnim(str) {
str = string_trim(str);
var strs = string_splice(str, ".");
if(array_length(strs) == 0) return 0;
if(array_length(strs) == 1) {
return EXPRESS_TREE_ANIM.none;
} else if(struct_has(PROJECT_VARIABLES, strs[0])) {
var _str_var = PROJECT_VARIABLES[$ strs[0]];
if(!struct_has(_str_var, strs[1])) return EXPRESS_TREE_ANIM.none;
var val = _str_var[$ strs[1]][1];
return val;
} else if(array_length(strs) > 2) {
var key = strs[0];
if(!ds_map_exists(PROJECT.nodeNameMap, key)) return EXPRESS_TREE_ANIM.none;
var node = PROJECT.nodeNameMap[? key];
var map = noone;
switch(string_lower(strs[1])) {
case "inputs" :
case "input" :
map = node.inputMap;
break;
case "outputs" :
case "output" :
map = node.outputMap;
break;
default : return EXPRESS_TREE_ANIM.none;
}
var _junc_key = string_lower(strs[2]);
var _junc = ds_map_try_get(map, _junc_key, noone);
if(_junc == noone) return EXPRESS_TREE_ANIM.none;
return _junc.is_anim * 2;
}
return EXPRESS_TREE_ANIM.none;
}
function create_preview_window(node) {
if(node == noone) return;
var win = new Panel_Preview_Window();
win.node_target = node;
win.preview_channel = node.preview_channel;
var dia = dialogPanelCall(win, mouse_mx, mouse_my);
dia.destroy_on_click_out = false;
}
#endregion

View file

@ -0,0 +1,305 @@
// 2024-04-20 08:21:22
#macro DEF_SURF_W PROJECT.attributes.surface_dimension[0]
#macro DEF_SURF_H PROJECT.attributes.surface_dimension[1]
#macro DEF_SURF PROJECT.attributes.surface_dimension
#macro DEF_PALETTE PROJECT.attributes.palette
#region
function node_draw_transform_init() {
drag_type = -1;
dragging_sx = 0;
dragging_sy = 0;
dragging_mx = 0;
dragging_my = 0;
rot_anc_x = 0;
rot_anc_y = 0;
}
function node_draw_transform_box(active, _x, _y, _s, _mx, _my, _snx, _sny, _posInd, _rotInd, _scaInd, _scaUnit = false) {
var _pos = getInputData(_posInd);
var _rot = getInputData(_rotInd);
var _sca = getInputData(_scaInd);
if(drag_type > -1) {
if(drag_type == 0) {
var _dx = (_mx - dragging_mx) / _s;
var _dy = (_my - dragging_my) / _s;
if(key_mod_press(SHIFT)) {
if(abs(_dx) > abs(_dy) + ui(16))
_dy = 0;
else if(abs(_dy) > abs(_dx) + ui(16))
_dx = 0;
else {
_dx = max(_dx, _dy);
_dy = _dx;
}
}
_pos[0] = value_snap(dragging_sx + _dx, _snx);
_pos[1] = value_snap(dragging_sy + _dy, _sny);
if(inputs[| _posInd].setValue(_pos))
UNDO_HOLDING = true;
if(inputs[| _posInd].unit.mode == VALUE_UNIT.reference) {
var p = [ _pos[0], _pos[1] ];
_pos = inputs[| _posInd].unit.apply(p);
}
} else if(drag_type == 1) {
var aa = point_direction(rot_anc_x, rot_anc_y, _mx, _my);
var da = angle_difference(dragging_mx, aa);
if(key_mod_press(CTRL))
_rot = round((dragging_sx - da) / 15) * 15;
else
_rot = dragging_sx - da;
if(inputs[| _rotInd].setValue(_rot))
UNDO_HOLDING = true;
} else if(drag_type == 2) {
var _p = point_rotate(_mx - dragging_mx, _my - dragging_my, 0, 0, -_rot);
_sca[0] = _p[0] / _s;
_sca[1] = _p[1] / _s;
if(key_mod_press(SHIFT)) {
_sca[0] = min(_sca[0], _sca[1]);
_sca[1] = min(_sca[0], _sca[1]);
}
if(inputs[| _scaInd].setValue(_sca))
UNDO_HOLDING = true;
if(_scaUnit && inputs[| _scaInd].unit.mode == VALUE_UNIT.reference) {
var s = [ _sca[0], _sca[1] ];
_sca = inputs[| _scaInd].unit.apply(s);
}
}
if(mouse_release(mb_left)) {
drag_type = -1;
UNDO_HOLDING = false;
}
}
var p0 = point_rotate(-_sca[0], -_sca[1], 0, 0, _rot);
var p1 = point_rotate( _sca[0], -_sca[1], 0, 0, _rot);
var p2 = point_rotate(-_sca[0], _sca[1], 0, 0, _rot);
var p3 = point_rotate( _sca[0], _sca[1], 0, 0, _rot);
var pr = point_rotate( 0, -_sca[1] - 1, 0, 0, _rot);
var pd0x = _x + (p0[0] + _pos[0]) * _s; var pd0y = _y + (p0[1] + _pos[1]) * _s;
var pd1x = _x + (p1[0] + _pos[0]) * _s; var pd1y = _y + (p1[1] + _pos[1]) * _s;
var pd2x = _x + (p2[0] + _pos[0]) * _s; var pd2y = _y + (p2[1] + _pos[1]) * _s;
var pd3x = _x + (p3[0] + _pos[0]) * _s; var pd3y = _y + (p3[1] + _pos[1]) * _s;
var prx = _x + (pr[0] + _pos[0]) * _s; var pry = _y + (pr[1] + _pos[1]) * _s;
var hovering = -1;
if(drag_type == -1) {
if(point_in_rectangle_points(_mx, _my, pd0x, pd0y, pd1x, pd1y, pd2x, pd2y, pd3x, pd3y))
hovering = 0;
if(point_in_circle(_mx, _my, prx, pry, 12))
hovering = 1;
if(point_in_circle(_mx, _my, pd3x, pd3y, 12))
hovering = 2;
}
draw_set_color(COLORS._main_accent);
draw_line_width(pd0x, pd0y, pd1x, pd1y, hovering == 0? 2 : 1);
draw_line_width(pd0x, pd0y, pd2x, pd2y, hovering == 0? 2 : 1);
draw_line_width(pd3x, pd3y, pd1x, pd1y, hovering == 0? 2 : 1);
draw_line_width(pd3x, pd3y, pd2x, pd2y, hovering == 0? 2 : 1);
draw_sprite_colored(THEME.anchor_rotate, hovering == 1, prx, pry,, _rot);
draw_sprite_colored(THEME.anchor_scale, hovering == 2, pd3x, pd3y,, _rot);
if(hovering == -1) return;
if(drag_type > -1) return;
if(mouse_press(mb_left, active)) {
drag_type = hovering;
if(hovering == 0) {
dragging_sx = _pos[0];
dragging_sy = _pos[1];
dragging_mx = _mx;
dragging_my = _my;
} else if(hovering == 1) { //rot
dragging_sx = _rot;
rot_anc_x = _x + _pos[0] * _s;
rot_anc_y = _y + _pos[1] * _s;
dragging_mx = point_direction(rot_anc_x, rot_anc_y, _mx, _my);
} else if(hovering == 2) { //sca
dragging_sx = _sca[0];
dragging_sy = _sca[1];
dragging_mx = _x + _pos[0] * _s;
dragging_my = _y + _pos[1] * _s;
}
}
}
#endregion
#region node function
function nodeLoad(_data, scale = false, _group = noone) {
INLINE
if(!is_struct(_data)) return;
var _x = _data.x;
var _y = _data.y;
var _type = _data.type;
if(ds_map_exists(APPEND_MAP, _data.id)) {
var _node = APPEND_MAP[? _data.id];
_node.x = _x;
_node.y = _y;
_node.deserialize(_data, scale);
return _node;
}
var _node = nodeBuild(_type, _x, _y, _group);
if(_node) _node.deserialize(_data, scale);
return _node;
}
function nodeCleanUp() {
var key = ds_map_find_first(PROJECT.nodeMap);
repeat(ds_map_size(PROJECT.nodeMap)) {
if(PROJECT.nodeMap[? key]) {
PROJECT.nodeMap[? key].active = false;
PROJECT.nodeMap[? key].cleanUp();
delete PROJECT.nodeMap[? key];
}
key = ds_map_find_next(PROJECT.nodeMap, key);
}
ds_map_clear(APPEND_MAP);
}
function graphFocusNode(node) {
PANEL_INSPECTOR.setInspecting(node);
PANEL_GRAPH.nodes_selecting = [ node ];
PANEL_GRAPH.fullView();
}
function refreshNodeMap() {
ds_map_clear(PROJECT.nodeNameMap);
var key = ds_map_find_first(PROJECT.nodeMap);
var amo = ds_map_size(PROJECT.nodeMap);
repeat(amo) {
var node = PROJECT.nodeMap[? key];
if(node.internalName != "")
PROJECT.nodeNameMap[? node.internalName] = node;
key = ds_map_find_next(PROJECT.nodeMap, key);
}
}
function nodeGetData(str) {
str = string_trim(str);
var strs = string_splice(str, ".");
if(array_length(strs) == 0) return 0;
if(array_length(strs) == 1) {
var splt = string_splice(strs[0], "[");
var inp = PROJECT.globalNode.getInput(strs[0]);
if(inp == 0) return 0;
var _arr = [ 0, 0 ];
inp.getValueRecursive(_arr);
return _arr[0];
} else if(struct_has(PROJECT_VARIABLES, strs[0])) {
var _str_var = PROJECT_VARIABLES[$ strs[0]];
if(!struct_has(_str_var, strs[1])) return 0;
var val = _str_var[$ strs[1]][0];
if(is_callable(val))
return val();
return val;
} else if(array_length(strs) > 2) {
var key = strs[0];
if(!ds_map_exists(PROJECT.nodeNameMap, key)) return 0;
var node = PROJECT.nodeNameMap[? key];
var map = noone;
switch(string_lower(strs[1])) {
case "inputs" :
case "input" :
map = node.inputMap;
break;
case "outputs" :
case "output" :
map = node.outputMap;
break;
default : return 0;
}
var _junc_key = string_lower(strs[2]);
var _junc = ds_map_try_get(map, _junc_key, noone);
if(_junc == noone) return 0;
return _junc.getValue();
}
return 0;
}
function nodeGetDataAnim(str) {
str = string_trim(str);
var strs = string_splice(str, ".");
if(array_length(strs) == 0) return 0;
if(array_length(strs) == 1) {
return EXPRESS_TREE_ANIM.none;
} else if(struct_has(PROJECT_VARIABLES, strs[0])) {
var _str_var = PROJECT_VARIABLES[$ strs[0]];
if(!struct_has(_str_var, strs[1])) return EXPRESS_TREE_ANIM.none;
var val = _str_var[$ strs[1]][1];
return val;
} else if(array_length(strs) > 2) {
var key = strs[0];
if(!ds_map_exists(PROJECT.nodeNameMap, key)) return EXPRESS_TREE_ANIM.none;
var node = PROJECT.nodeNameMap[? key];
var map = noone;
switch(string_lower(strs[1])) {
case "inputs" :
case "input" :
map = node.inputMap;
break;
case "outputs" :
case "output" :
map = node.outputMap;
break;
default : return EXPRESS_TREE_ANIM.none;
}
var _junc_key = string_lower(strs[2]);
var _junc = ds_map_try_get(map, _junc_key, noone);
if(_junc == noone) return EXPRESS_TREE_ANIM.none;
return _junc.is_anim * 2;
}
return EXPRESS_TREE_ANIM.none;
}
function create_preview_window(node) {
if(node == noone) return;
var win = new Panel_Preview_Window();
win.node_target = node;
win.preview_channel = node.preview_channel;
var dia = dialogPanelCall(win, mouse_mx, mouse_my);
dia.destroy_on_click_out = false;
}
#endregion

View file

@ -0,0 +1,740 @@
// 2024-04-20 09:03:18
enum KEY_TYPE { normal, adder }
enum CURVE_TYPE { linear, bezier, cut }
enum DRIVER_TYPE { none, linear, wiggle, sine }
function valueKey(_time, _value, _anim = noone, _in = 0, _ot = 0) constructor {
#region ---- main ----
time = _time;
ratio = time / (TOTAL_FRAMES - 1);
value = _value;
anim = _anim;
ease_y_lock = true;
ease_in = is_array(_in)? _in : [_in, 1];
ease_out = is_array(_ot)? _ot : [_ot, 0];
var _int = anim? anim.prop.key_inter : CURVE_TYPE.linear;
ease_in_type = _int;
ease_out_type = _int;
dopesheet_x = 0;
drivers = {
seed : irandom_range(100000, 999999),
type : DRIVER_TYPE.none,
speed : 1,
octave : 2,
frequency : 4,
amplitude : 1,
axis_sync : false,
phase : 0,
};
#endregion
static setTime = function(time) { #region
self.time = time;
ratio = time / (TOTAL_FRAMES - 1);
} #endregion
static clone = function(target = noone) { #region
var key = new valueKey(time, value, target);
key.ease_in = ease_in;
key.ease_out = ease_out;
key.ease_in_type = ease_in_type;
key.ease_out_type = ease_out_type;
return key;
} #endregion
static cloneAnimator = function(shift = 0, anim = noone, removeDup = true) { #region
if(anim != noone) { //check value compat between animator
if(value_bit(self.anim.prop.type) & value_bit(anim.prop.type) == 0) {
noti_warning("Type incompatible");
return noone;
}
if(typeArray(self.anim.prop.display_type) != typeArray(anim.prop.display_type)) {
noti_warning("Type incompatible");
return noone;
}
}
if(anim == noone) anim = self.anim;
var key = new valueKey(time + shift, value, anim);
key.ease_in = ease_in;
key.ease_out = ease_out;
key.ease_in_type = ease_in_type;
key.ease_out_type = ease_out_type;
ds_list_add(anim.values, key);
anim.setKeyTime(key, time + shift, removeDup);
return key;
} #endregion
static getDrawIndex = function() { #region
if(anim.prop.type == VALUE_TYPE.trigger)
return 1;
if(drivers.type)
return 2;
if(ease_in_type == CURVE_TYPE.cut)
return 1;
return 0;
} #endregion
static toString = function() { return $"[Keyframe] {time}: {value}"; }
}
function valueAnimator(_val, _prop, _sep_axis = false) constructor {
#region ---- main ----
suffix = "";
values = ds_list_create();
//staticValue = 0;
length = 1;
sep_axis = _sep_axis;
index = 0;
prop = _prop;
y = 0;
key_map = array_create(TOTAL_FRAMES);
key_map_mode = KEYFRAME_END.hold;
animate_frames = [];
if(_prop.type != VALUE_TYPE.trigger)
ds_list_add(values, new valueKey(0, _val, self));
#endregion
static refreshAnimation = function() { #region
animate_frames = array_verify(animate_frames, TOTAL_FRAMES);
var _anim = false;
var _fr = noone;
for( var i = 0, n = ds_list_size(values); i < n; i++ ) {
var _key = values[| i];
if(_fr == noone) {
array_fill(animate_frames, 0, _key.time, 0);
} else {
if(array_equals(_fr.ease_out, [0, 0]) && array_equals(_fr.ease_in, [0, 1]) && isEqual(_fr.value, _key.value))
array_fill(animate_frames, _fr.time, _key.time, 0);
else
array_fill(animate_frames, _fr.time, _key.time, 1);
}
_fr = _key;
}
if(_fr) array_fill(animate_frames, _fr.time, TOTAL_FRAMES, 0);
} #endregion
static updateKeyMap = function() { #region
length = ds_list_size(values);
if(!prop.is_anim && !LOADING && !APPENDING) return;
if(ds_list_empty(values)) {
array_resize(key_map, TOTAL_FRAMES);
return;
}
var _len = max(TOTAL_FRAMES, values[| ds_list_size(values) - 1].time);
key_map_mode = prop.on_end;
if(array_length(key_map) != _len)
array_resize(key_map, _len);
if(prop.type == VALUE_TYPE.trigger) {
array_fill(key_map, 0, _len, 0);
for( var i = 0, n = ds_list_size(values); i < n; i++ )
key_map[values[| i].time] = true;
return;
}
if(ds_list_size(values) < 2) {
array_fill(key_map, 0, _len, 0);
return;
}
var _firstKey = values[| 0].time;
array_fill(key_map, 0, _firstKey, -1);
var _keyIndex = _firstKey;
for( var i = 1, n = ds_list_size(values); i < n; i++ ) {
var _k1 = values[| i].time;
array_fill(key_map, _keyIndex, _k1, i - 1);
_keyIndex = _k1;
}
array_fill(key_map, _keyIndex, _len, 999_999);
} #endregion
static interpolate = function(from, to, rat) { #region
if(prop.type == VALUE_TYPE.boolean)
return 0;
if(to.ease_in_type == CURVE_TYPE.linear && from.ease_out_type == CURVE_TYPE.linear)
return rat;
if(to.ease_in_type == CURVE_TYPE.cut)
return 0;
if(from.ease_out_type == CURVE_TYPE.cut)
return 1;
if(rat == 0 || rat == 1)
return rat;
var eox = clamp(from.ease_out[0], 0, 0.9);
var eix = clamp(to.ease_in[0], 0, 0.9);
var eoy = from.ease_out[1];
var eiy = to.ease_in[1];
var bz = [0, eox, eoy, 1. - eix, eiy, 1];
return eval_curve_segment_x(bz, rat);
} #endregion
static lerpValue = function(from, to, _lrp) { #region
var _f = from.value;
var _t = to.value;
if(is_struct(_f)) {
if(!struct_has(_f, "lerpTo")) return _f;
return _f.lerpTo(_t, _lrp);
}
if(prop.display_type == VALUE_DISPLAY.d3quarternion) {
if(prop.display_data.angle_display == 0) {
var _qf = new BBMOD_Quaternion(_f[0], _f[1], _f[2], _f[3]);
var _qt = new BBMOD_Quaternion(_t[0], _t[1], _t[2], _t[3]);
var _ql = _qf.Slerp(_qt, _lrp);
return _ql.ToArray();
} else {
return [
lerp(_f[0], _t[0], _lrp),
lerp(_f[1], _t[1], _lrp),
lerp(_f[2], _t[2], _lrp),
0,
];
}
}
if(prop.type == VALUE_TYPE.color) {
if(is_array(_f) && is_array(_t)) {
var _len = ceil(lerp(array_length(_f), array_length(_t), _lrp));
var res = array_create(_len);
for( var i = 0; i < _len; i++ ) {
var rat = i / (_len - 1);
var rf = rat * (array_length(_f) - 1);
var rt = rat * (array_length(_t) - 1);
var cf = array_get_decimal(_f, rf, true);
var ct = array_get_decimal(_t, rt, true);
res[i] = merge_color(cf, ct, _lrp);
}
return res;
}
return processType(merge_color(_f, _t, _lrp));
}
if(is_array(_f) || is_array(_t)) {
var _len = max(array_safe_length(_f), array_safe_length(_t));
var _vec = array_create(_len);
for(var i = 0; i < _len; i++)
_vec[i] = processType(
lerp(
is_array(_f)? array_safe_get_fast(_f, i, 0) : _f,
is_array(_t)? array_safe_get_fast(_t, i, 0) : _t,
_lrp)
);
return _vec;
}
if(prop.type == VALUE_TYPE.text)
return processType(_f);
return processType(lerp(_f, _t, _lrp));
} #endregion
static getName = function() { return prop.name + suffix; }
static getValue = function(_time = CURRENT_FRAME) { #region
//if(!prop.is_anim) return staticValue;
length = ds_list_size(values);
///////////////////////////////////////////////////////////// TRIGGER TYPE /////////////////////////////////////////////////////////////
if(prop.type == VALUE_TYPE.trigger) {
if(length == 0 || !prop.is_anim) return false;
if(array_length(key_map) != TOTAL_FRAMES) updateKeyMap();
return key_map[_time];
}
///////////////////////////////////////////////////////////// OPTIMIZATION /////////////////////////////////////////////////////////////
if(length == 0) return processTypeDefault();
if(length == 1) {
var _key = values[| 0];
if(_key.drivers.type && _time >= _key.time)
return processType(processDriver(_time, _key));
return processType(_key.value);
}
if(prop.type == VALUE_TYPE.path) return processType(values[| 0].value);
if(!prop.is_anim) return processType(values[| 0].value);
var _len = max(TOTAL_FRAMES, values[| length - 1].time);
if(array_length(key_map) != _len) updateKeyMap();
var _time_first = prop.loop_range == -1? values[| 0].time : values[| length - 1 - prop.loop_range].time;
var _time_last = values[| length - 1].time;
var _time_dura = _time_last - _time_first;
////////////////////////////////////////////////////////////// LOOP TIME ///////////////////////////////////////////////////////////////
if(_time > _time_last) {
switch(prop.on_end) {
case KEYFRAME_END.loop :
_time = _time_first + safe_mod(_time - _time_last, _time_dura + 1);
break;
case KEYFRAME_END.ping :
var time_in_loop = safe_mod(_time - _time_first, _time_dura * 2);
if(time_in_loop < _time_dura)
_time = _time_first + time_in_loop;
else
_time = _time_first + _time_dura * 2 - time_in_loop;
break;
}
}
var _keyIndex;
if(_time >= _len) _keyIndex = 999_999;
else if(_time <= 0) _keyIndex = -1;
else _keyIndex = array_safe_get_fast(key_map, _time);
//////////////////////////////////////////////////////////// BEFORE FIRST //////////////////////////////////////////////////////////////
if(_keyIndex == -1) {
if(prop.on_end == KEYFRAME_END.wrap) {
var from = values[| length - 1];
var to = values[| 0];
var fTime = from.time;
var tTime = to.time;
var prog = TOTAL_FRAMES - fTime + _time;
var totl = TOTAL_FRAMES - fTime + tTime;
var rat = prog / totl;
var _lrp = interpolate(from, to, rat);
return lerpValue(from, to, _lrp);
}
return processType(values[| 0].value); //First frame
}
///////////////////////////////////////////////////////////// AFTER LAST ///////////////////////////////////////////////////////////////
if(_keyIndex == 999_999) {
var _lstKey = values[| length - 1];
if(_lstKey.drivers.type)
return processType(processDriver(_time, _lstKey));
if(prop.on_end == KEYFRAME_END.wrap) {
var from = _lstKey;
var to = values[| 0];
var prog = _time - from.time;
var totl = TOTAL_FRAMES - from.time + to.time;
var rat = prog / totl;
var _lrp = interpolate(from, to, rat);
return lerpValue(from, to, _lrp);
}
return processType(_lstKey.value); //Last frame
}
///////////////////////////////////////////////////////////// INBETWEEN ////////////////////////////////////////////////////////////////
var from = values[| _keyIndex];
var to = values[| _keyIndex + 1];
var rat = (_time - from.time) / (to.time - from.time);
var _lrp = interpolate(from, to, rat);
if(from.drivers.type)
return processDriver(_time, from, lerpValue(from, to, _lrp), rat);
return lerpValue(from, to, _lrp);
} #endregion
static processTypeDefault = function() { #region
if(!sep_axis && typeArray(prop.display_type)) return [];
return 0;
} #endregion
static processDriver = function(_time, _key, _val = undefined, _intp = 0) { #region
static _processDriver = function(val, drivers, _t, _index = 0, _intp = 0) {
switch(drivers.type) {
case DRIVER_TYPE.linear :
return val + _t * drivers.speed;
case DRIVER_TYPE.wiggle :
var w = perlin1D(_t, drivers.seed + _index, drivers.frequency / 10, drivers.octave, -1, 1) * drivers.amplitude;
return val + w;
case DRIVER_TYPE.sine :
var w = sin((drivers.phase * (_index + 1) + _t * drivers.frequency / TOTAL_FRAMES) * pi * 2) * drivers.amplitude;
return val + w;
}
return 0;
}
var _dt = _time - _key.time;
_val = _val == undefined? _key.value : _val;
var _res = _val;
if(prop.type == VALUE_TYPE.integer || prop.type == VALUE_TYPE.float) {
if(is_array(_val)) {
_res = array_create(array_length(_val));
for( var i = 0, n = array_length(_val); i < n; i++ )
_res[i] = is_numeric(_val[i])? _processDriver(_val[i], _key.drivers, _dt, _key.drivers.axis_sync? 0 : i, _intp) : _val[i];
} else
_res = _processDriver(_val, _key.drivers, _dt, 0, _intp);
}
return _res;
} #endregion
static processType = function(_val) { #region
INLINE
var _res = _val;
if(!sep_axis && typeArray(prop.display_type) && is_array(_val)) {
for(var i = 0; i < array_length(_val); i++)
_res[i] = processValue(_val[i]);
} else
_res = processValue(_val);
return _res;
} #endregion
static processValue = function(_val) { #region
INLINE
if(is_array(_val)) return _val;
if(is_struct(_val)) return _val;
//if(is_undefined(_val)) return 0;
switch(prop.type) {
case VALUE_TYPE.integer : return prop.unit.mode == VALUE_UNIT.constant? round(_val) : _val;
case VALUE_TYPE.float : return _val;
case VALUE_TYPE.text : return is_string(_val)? _val : string_real(_val);
case VALUE_TYPE.color : return is_real(_val)? cola(_val) : _val;
case VALUE_TYPE.surface : return is_string(_val)? get_asset(_val) : _val;
}
return _val;
} #endregion
static insertKey = function(_key, _index) { ds_list_insert(values, _index, _key); }
static setKeyTime = function(_key, _time, _replace = true, record = false) { #region
if(!ds_list_exist(values, _key)) return 0;
if(_key.time == _time && !_replace) return 0;
if(!LOADING) PROJECT.modified = true;
var _prevTime = _key.time;
_time = max(_time, 0);
_key.setTime(_time);
ds_list_remove(values, _key);
if(_replace)
for( var i = 0; i < ds_list_size(values); i++ ) {
if(values[| i].time != _time) continue;
if(record) {
var act = new Action(ACTION_TYPE.custom, function(data) {
if(data.undo) insertKey(data.overKey, data.index);
updateKeyMap();
data.undo = !data.undo;
}, { overKey : values[| i], index : i, undo : true });
mergeAction(act);
}
values[| i] = _key;
updateKeyMap();
return 2;
}
for( var i = 0; i < ds_list_size(values); i++ ) { //insert key before the last key
if(values[| i].time < _time) continue;
if(record) recordAction(ACTION_TYPE.custom, function(data) {
var _prevTime = data.key.time;
setKeyTime(data.key, data.time, false);
data.time = _prevTime;
}, { key : _key, time : _prevTime });
ds_list_insert(values, i, _key);
if(_replace) updateKeyMap();
return 1;
}
if(record) recordAction(ACTION_TYPE.custom, function(data) { // insert key after the last key
var _prevTime = data.key.time;
setKeyTime(data.key, data.time, false);
data.time = _prevTime;
}, { key : _key, time : _prevTime });
ds_list_add(values, _key);
if(_replace) updateKeyMap();
return 1;
} #endregion
static setValue = function(_val = 0, _record = true, _time = CURRENT_FRAME, ease_in = 0, ease_out = 0) { #region
//staticValue = _val;
if(prop.type == VALUE_TYPE.trigger) {
if(!prop.is_anim) {
values[| 0] = new valueKey(0, _val, self);
updateKeyMap();
return true;
}
for(var i = 0; i < ds_list_size(values); i++) { //Find trigger
var _key = values[| i];
if(_key.time == _time) {
if(!global.FLAG.keyframe_override) return false;
_key.value = _val;
return false;
} else if(_key.time > _time) {
ds_list_insert(values, i, new valueKey(_time, _val, self));
updateKeyMap();
return true;
}
}
//print($"{_time}: {_val} | Insert last");
ds_list_add(values, new valueKey(_time, _val, self));
updateKeyMap();
return true;
}
if(!prop.is_anim) {
if(isEqual(values[| 0].value, _val))
return false;
if(_record) recordAction(ACTION_TYPE.var_modify, values[| 0], [ values[| 0].value, "value", prop.name ]);
values[| 0].value = _val;
return true;
}
if(ds_list_size(values) == 0) { // Should not be called normally
var k = new valueKey(_time, _val, self, ease_in, ease_out);
ds_list_add(values, k);
if(_record) recordAction(ACTION_TYPE.list_insert, values, [ k, ds_list_size(values) - 1, $"add {prop.name} keyframe" ], function() { updateKeyMap(); });
return true;
}
for(var i = 0; i < ds_list_size(values); i++) {
var _key = values[| i];
if(_key.time == _time) {
if(!global.FLAG.keyframe_override) return false;
if(_key.value != _val) {
if(_record) recordAction(ACTION_TYPE.var_modify, _key, [ _key.value, "value", prop.name ]);
_key.value = _val;
return true;
}
return false;
} else if(_key.time > _time) {
var k = new valueKey(_time, _val, self, ease_in, ease_out);
ds_list_insert(values, i, k);
if(_record) recordAction(ACTION_TYPE.list_insert, values, [k, i, $"add {prop.name} keyframe" ], function() { updateKeyMap(); });
updateKeyMap();
return true;
}
}
var k = new valueKey(_time, _val, self, ease_in, ease_out);
if(_record) recordAction(ACTION_TYPE.list_insert, values, [ k, ds_list_size(values), $"add {prop.name} keyframe" ], function() { updateKeyMap(); });
ds_list_add(values, k);
updateKeyMap();
return true;
} #endregion
static removeKey = function(key) { #region
if(ds_list_size(values) > 1)
ds_list_remove(values, key);
else
prop.is_anim = false;
updateKeyMap();
} #endregion
static serialize = function(scale = false) { #region
var _data = [];
for(var i = 0; i < ds_list_size(values); i++) {
var _value_list = [];
if(scale)
_value_list[0] = values[| i].time / (TOTAL_FRAMES - 1);
else
_value_list[0] = values[| i].time;
var val = values[| i].value;
if(prop.type == VALUE_TYPE.struct) {
val = json_stringify(val);
} else if(is_struct(val)) {
val = val.serialize();
} else if(!sep_axis && typeArray(prop.display_type) && is_array(val)) {
var __v = [];
for(var j = 0; j < array_length(val); j++) {
if(is_struct(val[j]) && struct_has(val[j], "serialize"))
array_push(__v, val[j].serialize());
else
array_push(__v, val[j]);
}
val = __v;
}
_value_list[1] = val;
_value_list[2] = values[| i].ease_in;
_value_list[3] = values[| i].ease_out;
_value_list[4] = values[| i].ease_in_type;
_value_list[5] = values[| i].ease_out_type;
_value_list[6] = values[| i].ease_y_lock;
_value_list[7] = values[| i].drivers;
array_push(_data, _value_list);
}
return _data;
} #endregion
static deserialize = function(_data, scale = false) { #region
ds_list_clear(values);
if(prop.type == VALUE_TYPE.gradient && LOADING_VERSION < 1340 && !CLONING) { #region //backward compat: Gradient
var _val = [];
var value = _data[0][1];
if(is_array(value))
for(var i = 0; i < array_length(value); i++) {
var _keyframe = value[i];
var _t = struct_try_get(_keyframe, "time");
var _v = struct_try_get(_keyframe, "value");
array_push(_val, new gradientKey(_t, _v));
}
var grad = new gradientObject();
grad.keys = _val;
ds_list_add(values, new valueKey(0, grad, self));
updateKeyMap();
return;
} #endregion
var base = prop.def_val;
for(var i = 0; i < array_length(_data); i++) {
var _keyframe = _data[i];
var _time = array_safe_get_fast(_keyframe, 0);
if(scale && _time <= 1)
_time = round(_time * (TOTAL_FRAMES - 1));
var value = array_safe_get_fast(_keyframe, 1);
var ease_in = array_safe_get_fast(_keyframe, 2);
var ease_out = array_safe_get_fast(_keyframe, 3);
var ease_in_type = array_safe_get_fast(_keyframe, 4);
var ease_out_type = array_safe_get_fast(_keyframe, 5);
var ease_y_lock = array_safe_get_fast(_keyframe, 6, true);
var driver = array_safe_get_fast(_keyframe, 7, {});
var _val = value;
if(prop.type == VALUE_TYPE.struct)
_val = json_try_parse(value);
else if(prop.type == VALUE_TYPE.path && prop.display_type == VALUE_DISPLAY.path_array) {
for(var j = 0; j < array_length(value); j++)
_val[j] = value[j];
} else if(prop.type == VALUE_TYPE.gradient) {
var grad = new gradientObject();
_val = grad.deserialize(value);
} else if(prop.type == VALUE_TYPE.d3Material) {
var mat = new __d3dMaterial();
_val = mat.deserialize(value);
} else if(prop.type == VALUE_TYPE.color) {
if(is_array(_val)) {
for( var i = 0, n = array_length(_val); i < n; i++ )
_val[i] = LOADING_VERSION < 11640 && !is_int64(_val[i])? cola(_val[i]) : int64(_val[i]);
} else
_val = LOADING_VERSION < 11640 && !is_int64(_val)? cola(_val) : int64(_val);
} else if(!sep_axis && typeArray(prop.display_type)) {
_val = [];
if(is_array(value)) {
for(var j = 0; j < array_length(value); j++)
_val[j] = processValue(value[j]);
} else if(is_array(base)) {
for(var j = 0; j < array_length(base); j++)
_val[j] = processValue(value);
}
}
//print($"Deserialize {prop.node.name}:{prop.name} = {_val} ");
var vk = new valueKey(_time, _val, self, ease_in, ease_out);
vk.ease_in_type = ease_in_type;
vk.ease_out_type = ease_out_type;
vk.ease_y_lock = ease_y_lock;
struct_override(vk.drivers, driver);
ds_list_add(values, vk);
}
//staticValue = ds_list_empty(values)? 0 : values[| 0].value;
updateKeyMap();
} #endregion
static cleanUp = function() { #region
ds_list_destroy(values);
} #endregion
}

View file

@ -0,0 +1,740 @@
// 2024-04-20 09:02:54
enum KEY_TYPE { normal, adder }
enum CURVE_TYPE { linear, bezier, cut }
enum DRIVER_TYPE { none, linear, wiggle, sine }
function valueKey(_time, _value, _anim = noone, _in = 0, _ot = 0) constructor {
#region ---- main ----
time = _time;
ratio = time / (TOTAL_FRAMES - 1);
value = _value;
anim = _anim;
ease_y_lock = true;
ease_in = is_array(_in)? _in : [_in, 1];
ease_out = is_array(_ot)? _ot : [_ot, 0];
var _int = anim? anim.prop.key_inter : CURVE_TYPE.linear;
ease_in_type = _int;
ease_out_type = _int;
dopesheet_x = 0;
drivers = {
seed : irandom_range(100000, 999999),
type : DRIVER_TYPE.none,
speed : 1,
octave : 2,
frequency : 4,
amplitude : 1,
axis_sync : false,
phase : 0,
};
#endregion
static setTime = function(time) { #region
self.time = time;
ratio = time / (TOTAL_FRAMES - 1);
} #endregion
static clone = function(target = noone) { #region
var key = new valueKey(time, value, target);
key.ease_in = ease_in;
key.ease_out = ease_out;
key.ease_in_type = ease_in_type;
key.ease_out_type = ease_out_type;
return key;
} #endregion
static cloneAnimator = function(shift = 0, anim = noone, removeDup = true) { #region
if(anim != noone) { //check value compat between animator
if(value_bit(self.anim.prop.type) & value_bit(anim.prop.type) == 0) {
noti_warning("Type incompatible");
return noone;
}
if(typeArray(self.anim.prop.display_type) != typeArray(anim.prop.display_type)) {
noti_warning("Type incompatible");
return noone;
}
}
if(anim == noone) anim = self.anim;
var key = new valueKey(time + shift, value, anim);
key.ease_in = ease_in;
key.ease_out = ease_out;
key.ease_in_type = ease_in_type;
key.ease_out_type = ease_out_type;
ds_list_add(anim.values, key);
anim.setKeyTime(key, time + shift, removeDup);
return key;
} #endregion
static getDrawIndex = function() { #region
if(anim.prop.type == VALUE_TYPE.trigger)
return 1;
if(drivers.type)
return 2;
if(ease_in_type == CURVE_TYPE.cut)
return 1;
return 0;
} #endregion
static toString = function() { return $"[Keyframe] {time}: {value}"; }
}
function valueAnimator(_val, _prop, _sep_axis = false) constructor {
#region ---- main ----
suffix = "";
values = ds_list_create();
//staticValue = 0;
length = 1;
sep_axis = _sep_axis;
index = 0;
prop = _prop;
y = 0;
key_map = array_create(TOTAL_FRAMES);
key_map_mode = KEYFRAME_END.hold;
animate_frames = [];
if(_prop.type != VALUE_TYPE.trigger)
ds_list_add(values, new valueKey(0, _val, self));
#endregion
static refreshAnimation = function() { #region
animate_frames = array_verify(animate_frames, TOTAL_FRAMES);
var _anim = false;
var _fr = noone;
for( var i = 0, n = ds_list_size(values); i < n; i++ ) {
var _key = values[| i];
if(_fr == noone) {
array_fill(animate_frames, 0, _key.time, 0);
} else {
if(array_equals(_fr.ease_out, [0, 0]) && array_equals(_fr.ease_in, [0, 1]) && isEqual(_fr.value, _key.value))
array_fill(animate_frames, _fr.time, _key.time, 0);
else
array_fill(animate_frames, _fr.time, _key.time, 1);
}
_fr = _key;
}
if(_fr) array_fill(animate_frames, _fr.time, TOTAL_FRAMES, 0);
} #endregion
static updateKeyMap = function() { #region
length = ds_list_size(values);
if(!prop.is_anim && !LOADING && !APPENDING) return;
if(ds_list_empty(values)) {
array_resize(key_map, TOTAL_FRAMES);
return;
}
var _len = max(TOTAL_FRAMES, values[| ds_list_size(values) - 1].time);
key_map_mode = prop.on_end;
if(array_length(key_map) != _len)
array_resize(key_map, _len);
if(prop.type == VALUE_TYPE.trigger) {
array_fill(key_map, 0, _len, 0);
for( var i = 0, n = ds_list_size(values); i < n; i++ )
key_map[values[| i].time] = true;
return;
}
if(ds_list_size(values) < 2) {
array_fill(key_map, 0, _len, 0);
return;
}
var _firstKey = values[| 0].time;
array_fill(key_map, 0, _firstKey, -1);
var _keyIndex = _firstKey;
for( var i = 1, n = ds_list_size(values); i < n; i++ ) {
var _k1 = values[| i].time;
array_fill(key_map, _keyIndex, _k1, i - 1);
_keyIndex = _k1;
}
array_fill(key_map, _keyIndex, _len, 999_999);
} #endregion
static interpolate = function(from, to, rat) { #region
if(prop.type == VALUE_TYPE.boolean)
return 0;
if(to.ease_in_type == CURVE_TYPE.linear && from.ease_out_type == CURVE_TYPE.linear)
return rat;
if(to.ease_in_type == CURVE_TYPE.cut)
return 0;
if(from.ease_out_type == CURVE_TYPE.cut)
return 1;
if(rat == 0 || rat == 1)
return rat;
var eox = clamp(from.ease_out[0], 0, 0.9);
var eix = clamp(to.ease_in[0], 0, 0.9);
var eoy = from.ease_out[1];
var eiy = to.ease_in[1];
var bz = [0, eox, eoy, 1. - eix, eiy, 1];
return eval_curve_segment_x(bz, rat);
} #endregion
static lerpValue = function(from, to, _lrp) { #region
var _f = from.value;
var _t = to.value;
if(is_struct(_f)) {
if(!struct_has(_f, "lerpTo")) return _f;
return _f.lerpTo(_t, _lrp);
}
if(prop.display_type == VALUE_DISPLAY.d3quarternion) {
if(prop.display_data.angle_display == 0) {
var _qf = new BBMOD_Quaternion(_f[0], _f[1], _f[2], _f[3]);
var _qt = new BBMOD_Quaternion(_t[0], _t[1], _t[2], _t[3]);
var _ql = _qf.Slerp(_qt, _lrp);
return _ql.ToArray();
} else {
return [
lerp(_f[0], _t[0], _lrp),
lerp(_f[1], _t[1], _lrp),
lerp(_f[2], _t[2], _lrp),
0,
];
}
}
if(prop.type == VALUE_TYPE.color) {
if(is_array(_f) && is_array(_t)) {
var _len = ceil(lerp(array_length(_f), array_length(_t), _lrp));
var res = array_create(_len);
for( var i = 0; i < _len; i++ ) {
var rat = i / (_len - 1);
var rf = rat * (array_length(_f) - 1);
var rt = rat * (array_length(_t) - 1);
var cf = array_get_decimal(_f, rf, true);
var ct = array_get_decimal(_t, rt, true);
res[i] = merge_color(cf, ct, _lrp);
}
return res;
}
return processType(merge_color(_f, _t, _lrp));
}
if(is_array(_f) || is_array(_t)) {
var _len = max(array_safe_length(_f), array_safe_length(_t));
var _vec = array_create(_len);
for(var i = 0; i < _len; i++)
_vec[i] = processType(
lerp(
is_array(_f)? array_safe_get_fast(_f, i, 0) : _f,
is_array(_t)? array_safe_get_fast(_t, i, 0) : _t,
_lrp)
);
return _vec;
}
if(prop.type == VALUE_TYPE.text)
return processType(_f);
return processType(lerp(_f, _t, _lrp));
} #endregion
static getName = function() { return prop.name + suffix; }
static getValue = function(_time = CURRENT_FRAME) { #region
//if(!prop.is_anim) return staticValue;
length = ds_list_size(values);
///////////////////////////////////////////////////////////// TRIGGER TYPE /////////////////////////////////////////////////////////////
if(prop.type == VALUE_TYPE.trigger) {
if(length == 0 || !prop.is_anim) return false;
if(array_length(key_map) != TOTAL_FRAMES) updateKeyMap();
return key_map[_time];
}
///////////////////////////////////////////////////////////// OPTIMIZATION /////////////////////////////////////////////////////////////
if(length == 0) return processTypeDefault();
if(length == 1) {
var _key = values[| 0];
if(_key.drivers.type && _time >= _key.time)
return processType(processDriver(_time, _key));
return processType(_key.value);
}
if(prop.type == VALUE_TYPE.path) return processType(values[| 0].value);
if(!prop.is_anim) return processType(values[| 0].value);
var _len = max(TOTAL_FRAMES, values[| length - 1].time);
if(array_length(key_map) != _len) updateKeyMap();
var _time_first = prop.loop_range == -1? values[| 0].time : values[| length - 1 - prop.loop_range].time;
var _time_last = values[| length - 1].time;
var _time_dura = _time_last - _time_first;
////////////////////////////////////////////////////////////// LOOP TIME ///////////////////////////////////////////////////////////////
if(_time > _time_last) {
switch(prop.on_end) {
case KEYFRAME_END.loop :
_time = _time_first + safe_mod(_time - _time_last, _time_dura + 1);
break;
case KEYFRAME_END.ping :
var time_in_loop = safe_mod(_time - _time_first, _time_dura * 2);
if(time_in_loop < _time_dura)
_time = _time_first + time_in_loop;
else
_time = _time_first + _time_dura * 2 - time_in_loop;
break;
}
}
var _keyIndex;
if(_time >= _len) _keyIndex = 999_999;
else if(_time <= 0) _keyIndex = -1;
else _keyIndex = array_safe_get_fast(key_map, _time);
//////////////////////////////////////////////////////////// BEFORE FIRST //////////////////////////////////////////////////////////////
if(_keyIndex == -1) {
if(prop.on_end == KEYFRAME_END.wrap) {
var from = values[| length - 1];
var to = values[| 0];
var fTime = from.time;
var tTime = to.time;
var prog = TOTAL_FRAMES - fTime + _time;
var totl = TOTAL_FRAMES - fTime + tTime;
var rat = prog / totl;
var _lrp = interpolate(from, to, rat);
return lerpValue(from, to, _lrp);
}
return processType(values[| 0].value); //First frame
}
///////////////////////////////////////////////////////////// AFTER LAST ///////////////////////////////////////////////////////////////
if(_keyIndex == 999_999) {
var _lstKey = values[| length - 1];
if(_lstKey.drivers.type)
return processType(processDriver(_time, _lstKey));
if(prop.on_end == KEYFRAME_END.wrap) {
var from = _lstKey;
var to = values[| 0];
var prog = _time - from.time;
var totl = TOTAL_FRAMES - from.time + to.time;
var rat = prog / totl;
var _lrp = interpolate(from, to, rat);
return lerpValue(from, to, _lrp);
}
return processType(_lstKey.value); //Last frame
}
///////////////////////////////////////////////////////////// INBETWEEN ////////////////////////////////////////////////////////////////
var from = values[| _keyIndex];
var to = values[| _keyIndex + 1];
var rat = (_time - from.time) / (to.time - from.time);
var _lrp = interpolate(from, to, rat);
if(from.drivers.type)
return processDriver(_time, from, lerpValue(from, to, _lrp), rat);
return lerpValue(from, to, _lrp);
} #endregion
static processTypeDefault = function() { #region
if(!sep_axis && typeArray(prop.display_type)) return [];
return 0;
} #endregion
static processDriver = function(_time, _key, _val = undefined, _intp = 0) { #region
static _processDriver = function(val, drivers, _t, _index = 0, _intp = 0) {
switch(drivers.type) {
case DRIVER_TYPE.linear :
return val + _t * drivers.speed;
case DRIVER_TYPE.wiggle :
var w = perlin1D(_t, drivers.seed + _index, drivers.frequency / 10, drivers.octave, -1, 1) * drivers.amplitude;
return val + w;
case DRIVER_TYPE.sine :
var w = sin((drivers.phase * (_index + 1) + _t * drivers.frequency / TOTAL_FRAMES) * pi * 2) * drivers.amplitude;
return val + w;
}
return 0;
}
var _dt = _time - _key.time;
_val = _val == undefined? _key.value : _val;
var _res = _val;
if(prop.type == VALUE_TYPE.integer || prop.type == VALUE_TYPE.float) {
if(is_array(_val)) {
_res = array_create(array_length(_val));
for( var i = 0, n = array_length(_val); i < n; i++ )
_res[i] = is_numeric(_val[i])? _processDriver(_val[i], _key.drivers, _dt, _key.drivers.axis_sync? 0 : i, _intp) : _val[i];
} else
_res = _processDriver(_val, _key.drivers, _dt, 0, _intp);
}
return _res;
} #endregion
static processType = function(_val) { #region
INLINE
var _res = _val;
if(!sep_axis && typeArray(prop.display_type) && is_array(_val)) {
for(var i = 0; i < array_length(_val); i++)
_res[i] = processValue(_val[i]);
} else
_res = processValue(_val);
return _res;
} #endregion
static processValue = function(_val) { #region
INLINE
if(is_array(_val)) return _val;
if(is_struct(_val)) return _val;
//if(is_undefined(_val)) return 0;
switch(prop.type) {
case VALUE_TYPE.integer : return prop.unit.mode == VALUE_UNIT.constant? round(_val) : _val;
case VALUE_TYPE.float : return _val;
case VALUE_TYPE.text : return is_string(_val)? _val : string_real(_val);
case VALUE_TYPE.color : return _val;
case VALUE_TYPE.surface : return is_string(_val)? get_asset(_val) : _val;
}
return _val;
} #endregion
static insertKey = function(_key, _index) { ds_list_insert(values, _index, _key); }
static setKeyTime = function(_key, _time, _replace = true, record = false) { #region
if(!ds_list_exist(values, _key)) return 0;
if(_key.time == _time && !_replace) return 0;
if(!LOADING) PROJECT.modified = true;
var _prevTime = _key.time;
_time = max(_time, 0);
_key.setTime(_time);
ds_list_remove(values, _key);
if(_replace)
for( var i = 0; i < ds_list_size(values); i++ ) {
if(values[| i].time != _time) continue;
if(record) {
var act = new Action(ACTION_TYPE.custom, function(data) {
if(data.undo) insertKey(data.overKey, data.index);
updateKeyMap();
data.undo = !data.undo;
}, { overKey : values[| i], index : i, undo : true });
mergeAction(act);
}
values[| i] = _key;
updateKeyMap();
return 2;
}
for( var i = 0; i < ds_list_size(values); i++ ) { //insert key before the last key
if(values[| i].time < _time) continue;
if(record) recordAction(ACTION_TYPE.custom, function(data) {
var _prevTime = data.key.time;
setKeyTime(data.key, data.time, false);
data.time = _prevTime;
}, { key : _key, time : _prevTime });
ds_list_insert(values, i, _key);
if(_replace) updateKeyMap();
return 1;
}
if(record) recordAction(ACTION_TYPE.custom, function(data) { // insert key after the last key
var _prevTime = data.key.time;
setKeyTime(data.key, data.time, false);
data.time = _prevTime;
}, { key : _key, time : _prevTime });
ds_list_add(values, _key);
if(_replace) updateKeyMap();
return 1;
} #endregion
static setValue = function(_val = 0, _record = true, _time = CURRENT_FRAME, ease_in = 0, ease_out = 0) { #region
//staticValue = _val;
if(prop.type == VALUE_TYPE.trigger) {
if(!prop.is_anim) {
values[| 0] = new valueKey(0, _val, self);
updateKeyMap();
return true;
}
for(var i = 0; i < ds_list_size(values); i++) { //Find trigger
var _key = values[| i];
if(_key.time == _time) {
if(!global.FLAG.keyframe_override) return false;
_key.value = _val;
return false;
} else if(_key.time > _time) {
ds_list_insert(values, i, new valueKey(_time, _val, self));
updateKeyMap();
return true;
}
}
//print($"{_time}: {_val} | Insert last");
ds_list_add(values, new valueKey(_time, _val, self));
updateKeyMap();
return true;
}
if(!prop.is_anim) {
if(isEqual(values[| 0].value, _val))
return false;
if(_record) recordAction(ACTION_TYPE.var_modify, values[| 0], [ values[| 0].value, "value", prop.name ]);
values[| 0].value = _val;
return true;
}
if(ds_list_size(values) == 0) { // Should not be called normally
var k = new valueKey(_time, _val, self, ease_in, ease_out);
ds_list_add(values, k);
if(_record) recordAction(ACTION_TYPE.list_insert, values, [ k, ds_list_size(values) - 1, $"add {prop.name} keyframe" ], function() { updateKeyMap(); });
return true;
}
for(var i = 0; i < ds_list_size(values); i++) {
var _key = values[| i];
if(_key.time == _time) {
if(!global.FLAG.keyframe_override) return false;
if(_key.value != _val) {
if(_record) recordAction(ACTION_TYPE.var_modify, _key, [ _key.value, "value", prop.name ]);
_key.value = _val;
return true;
}
return false;
} else if(_key.time > _time) {
var k = new valueKey(_time, _val, self, ease_in, ease_out);
ds_list_insert(values, i, k);
if(_record) recordAction(ACTION_TYPE.list_insert, values, [k, i, $"add {prop.name} keyframe" ], function() { updateKeyMap(); });
updateKeyMap();
return true;
}
}
var k = new valueKey(_time, _val, self, ease_in, ease_out);
if(_record) recordAction(ACTION_TYPE.list_insert, values, [ k, ds_list_size(values), $"add {prop.name} keyframe" ], function() { updateKeyMap(); });
ds_list_add(values, k);
updateKeyMap();
return true;
} #endregion
static removeKey = function(key) { #region
if(ds_list_size(values) > 1)
ds_list_remove(values, key);
else
prop.is_anim = false;
updateKeyMap();
} #endregion
static serialize = function(scale = false) { #region
var _data = [];
for(var i = 0; i < ds_list_size(values); i++) {
var _value_list = [];
if(scale)
_value_list[0] = values[| i].time / (TOTAL_FRAMES - 1);
else
_value_list[0] = values[| i].time;
var val = values[| i].value;
if(prop.type == VALUE_TYPE.struct) {
val = json_stringify(val);
} else if(is_struct(val)) {
val = val.serialize();
} else if(!sep_axis && typeArray(prop.display_type) && is_array(val)) {
var __v = [];
for(var j = 0; j < array_length(val); j++) {
if(is_struct(val[j]) && struct_has(val[j], "serialize"))
array_push(__v, val[j].serialize());
else
array_push(__v, val[j]);
}
val = __v;
}
_value_list[1] = val;
_value_list[2] = values[| i].ease_in;
_value_list[3] = values[| i].ease_out;
_value_list[4] = values[| i].ease_in_type;
_value_list[5] = values[| i].ease_out_type;
_value_list[6] = values[| i].ease_y_lock;
_value_list[7] = values[| i].drivers;
array_push(_data, _value_list);
}
return _data;
} #endregion
static deserialize = function(_data, scale = false) { #region
ds_list_clear(values);
if(prop.type == VALUE_TYPE.gradient && LOADING_VERSION < 1340 && !CLONING) { #region //backward compat: Gradient
var _val = [];
var value = _data[0][1];
if(is_array(value))
for(var i = 0; i < array_length(value); i++) {
var _keyframe = value[i];
var _t = struct_try_get(_keyframe, "time");
var _v = struct_try_get(_keyframe, "value");
array_push(_val, new gradientKey(_t, _v));
}
var grad = new gradientObject();
grad.keys = _val;
ds_list_add(values, new valueKey(0, grad, self));
updateKeyMap();
return;
} #endregion
var base = prop.def_val;
for(var i = 0; i < array_length(_data); i++) {
var _keyframe = _data[i];
var _time = array_safe_get_fast(_keyframe, 0);
if(scale && _time <= 1)
_time = round(_time * (TOTAL_FRAMES - 1));
var value = array_safe_get_fast(_keyframe, 1);
var ease_in = array_safe_get_fast(_keyframe, 2);
var ease_out = array_safe_get_fast(_keyframe, 3);
var ease_in_type = array_safe_get_fast(_keyframe, 4);
var ease_out_type = array_safe_get_fast(_keyframe, 5);
var ease_y_lock = array_safe_get_fast(_keyframe, 6, true);
var driver = array_safe_get_fast(_keyframe, 7, {});
var _val = value;
if(prop.type == VALUE_TYPE.struct)
_val = json_try_parse(value);
else if(prop.type == VALUE_TYPE.path && prop.display_type == VALUE_DISPLAY.path_array) {
for(var j = 0; j < array_length(value); j++)
_val[j] = value[j];
} else if(prop.type == VALUE_TYPE.gradient) {
var grad = new gradientObject();
_val = grad.deserialize(value);
} else if(prop.type == VALUE_TYPE.d3Material) {
var mat = new __d3dMaterial();
_val = mat.deserialize(value);
} else if(prop.type == VALUE_TYPE.color) {
if(is_array(_val)) {
for( var i = 0, n = array_length(_val); i < n; i++ )
_val[i] = LOADING_VERSION < 11640 && !is_int64(_val[i])? cola(_val[i]) : int64(_val[i]);
} else
_val = LOADING_VERSION < 11640 && !is_int64(_val)? cola(_val) : int64(_val);
} else if(!sep_axis && typeArray(prop.display_type)) {
_val = [];
if(is_array(value)) {
for(var j = 0; j < array_length(value); j++)
_val[j] = processValue(value[j]);
} else if(is_array(base)) {
for(var j = 0; j < array_length(base); j++)
_val[j] = processValue(value);
}
}
//print($"Deserialize {prop.node.name}:{prop.name} = {_val} ");
var vk = new valueKey(_time, _val, self, ease_in, ease_out);
vk.ease_in_type = ease_in_type;
vk.ease_out_type = ease_out_type;
vk.ease_y_lock = ease_y_lock;
struct_override(vk.drivers, driver);
ds_list_add(values, vk);
}
//staticValue = ds_list_empty(values)? 0 : values[| 0].value;
updateKeyMap();
} #endregion
static cleanUp = function() { #region
ds_list_destroy(values);
} #endregion
}

View file

@ -0,0 +1,53 @@
// 2024-04-20 09:44:05
function Node_Palette(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Palette";
setDimension(96);
inputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 1] = nodeValue("Trim range", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 1 ])
.setDisplay(VALUE_DISPLAY.slider_range);
outputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, [])
.setDisplay(VALUE_DISPLAY.palette);
input_display_list = [0,
["Trim", true], 1
];
static processData = function(_outSurf, _data, _output_index, _array_index) { #region
var pal = _data[0];
var ran = _data[1];
var st = floor(clamp(min(ran[0], ran[1]), 0, 1) * array_length(pal));
var en = floor(clamp(max(ran[0], ran[1]), 0, 1) * array_length(pal));
var ar = [];
for( var i = st; i < en; i++ )
ar[i - st] = array_safe_get_fast(pal, i);
return ar;
} #endregion
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
var bbox = drawGetBbox(xx, yy, _s);
if(bbox.h < 1) return;
var pal = outputs[| 0].getValue();
if(array_empty(pal)) return;
if(!is_array(pal[0])) pal = [ pal ];
var _h = array_length(pal) * 32;
var _y = bbox.y0;
var gh = bbox.h / array_length(pal);
for( var i = 0, n = array_length(pal); i < n; i++ ) {
drawPalette(pal[i], bbox.x0, _y, bbox.w, gh);
_y += gh;
}
if(_h != min_h) will_setHeight = true;
min_h = _h;
} #endregion
}

View file

@ -0,0 +1,63 @@
// 2024-04-20 09:23:40
function Node_Palette(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Palette";
setDimension(96);
inputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
var p = "";
var pl = inputs[| 0].animator.values[| 0].value;
for (var i = 0, n = array_length(pl); i < n; i++) p += number_to_hex(pl[i]) + ", ";
print(p);
var p = "";
var pl = DEF_PALETTE;
for (var i = 0, n = array_length(pl); i < n; i++) p += number_to_hex(pl[i]) + ", ";
print(p);
inputs[| 1] = nodeValue("Trim range", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 1 ])
.setDisplay(VALUE_DISPLAY.slider_range);
outputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, [])
.setDisplay(VALUE_DISPLAY.palette);
input_display_list = [0,
["Trim", true], 1
];
static processData = function(_outSurf, _data, _output_index, _array_index) { #region
var pal = _data[0];
var ran = _data[1];
var st = floor(clamp(min(ran[0], ran[1]), 0, 1) * array_length(pal));
var en = floor(clamp(max(ran[0], ran[1]), 0, 1) * array_length(pal));
var ar = [];
for( var i = st; i < en; i++ )
ar[i - st] = array_safe_get_fast(pal, i);
return ar;
} #endregion
static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { #region
var bbox = drawGetBbox(xx, yy, _s);
if(bbox.h < 1) return;
var pal = outputs[| 0].getValue();
if(array_empty(pal)) return;
if(!is_array(pal[0])) pal = [ pal ];
var _h = array_length(pal) * 32;
var _y = bbox.y0;
var gh = bbox.h / array_length(pal);
for( var i = 0, n = array_length(pal); i < n; i++ ) {
drawPalette(pal[i], bbox.x0, _y, bbox.w, gh);
_y += gh;
}
if(_h != min_h) will_setHeight = true;
min_h = _h;
} #endregion
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
// 2024-04-20 09:11:58
function string_variable_valid(str) {
static valid_char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
for( var i = 1; i <= string_length(str); i++ ) {
var cch = string_char_at(str, i);
if(string_pos(cch, valid_char) == 0) return false;
}
return true;
}
function string_decimal(str) {
var neg = string_char_at(str, 1) == "-";
if(neg) str = string_copy(str, 2, string_length(str) - 1);
var dec = string_pos(".", str);
if(dec == 0) return (neg? "-" : "") + string_digits(str);
var pre = string_copy(str, 1, dec - 1);
var pos = string_copy(str, dec + 1, string_length(str) - dec);
return (neg? "-" : "") + string_digits(pre) + "." + string_digits(pos);
}
function toNumber(str) {
INLINE
if(is_numeric(str)) return str;
try { return real(str); }
catch(e) {}
return 0;
}
//function toNumber(str) {
// INLINE
// if(is_real(str)) return str;
// if(!isNumber(str)) return 0;
// var expo = 0;
// if(string_pos("e", str)) {
// var pos = string_pos("e", str);
// expo = real(string_copy(str, pos + 1, string_length(str) - pos));
// }
// str = string_replace_all(str, ",", ".");
// str = string_decimal(str);
// if(str == "") return 0;
// if(str == ".") return 0;
// if(str == "-") return 0;
// return real(str) * power(10, expo);
//}
function isNumber(str) {
if(is_real(str)) return true;
str = string_trim(str);
return str == string_decimal(str);
}

View file

@ -0,0 +1,61 @@
// 2024-04-20 09:11:25
function string_variable_valid(str) {
static valid_char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
for( var i = 1; i <= string_length(str); i++ ) {
var cch = string_char_at(str, i);
if(string_pos(cch, valid_char) == 0) return false;
}
return true;
}
function string_decimal(str) {
var neg = string_char_at(str, 1) == "-";
if(neg) str = string_copy(str, 2, string_length(str) - 1);
var dec = string_pos(".", str);
if(dec == 0) return (neg? "-" : "") + string_digits(str);
var pre = string_copy(str, 1, dec - 1);
var pos = string_copy(str, dec + 1, string_length(str) - dec);
return (neg? "-" : "") + string_digits(pre) + "." + string_digits(pos);
}
function toNumber(str) {
INLINE
if(is_numeric(str)) return str;
try { return real(str); }
catch(e) {}
return 0;
}
//function toNumber(str) {
// INLINE
// if(is_real(str)) return str;
// if(!isNumber(str)) return 0;
// var expo = 0;
// if(string_pos("e", str)) {
// var pos = string_pos("e", str);
// expo = real(string_copy(str, pos + 1, string_length(str) - pos));
// }
// str = string_replace_all(str, ",", ".");
// str = string_decimal(str);
// if(str == "") return 0;
// if(str == ".") return 0;
// if(str == "-") return 0;
// return real(str) * power(10, expo);
//}
function isNumber(str) {
if(is_real(str)) return true;
str = string_trim(str);
return str == string_decimal(str);
}

View file

@ -0,0 +1,62 @@
// 2024-04-20 08:32:56
function string_hexadecimal(str) {
static HEX = "0123456789ABCDEF";
var i = string_length(str);
var d = 1;
var v = 0;
while(i > 0) {
var ch = string_char_at(str, i);
var val = string_pos(string_upper(ch), HEX) - 1;
v += val * d;
d *= 16;
i--;
}
return v;
}
function number_to_hex(val) {
static HEX = "0123456789ABCDEF";
var ss = "";
while(val > 0) {
var i = val % 16;
ss = string_char_at(HEX, i + 1) + ss;
val = floor(val / 16);
}
while(string_length(ss) < 2) {
ss = "0" + ss;
}
return ss;
}
function color_get_hex(color) {
var arr = is_array(color) && array_length(color) == 4;
var r = arr? round(color[0] * 256) : color_get_red(color);
var g = arr? round(color[1] * 256) : color_get_green(color);
var b = arr? round(color[2] * 256) : color_get_blue(color);
var a = arr? round(color[3] * 256) : color_get_alpha(color);
return number_to_hex(r) + number_to_hex(g) + number_to_hex(b) + (is_int64(color)? number_to_hex(a) : "");
}
function color_from_rgb(str) {
var _r = string_hexadecimal(string_copy(str, 1, 2));
var _g = string_hexadecimal(string_copy(str, 3, 2));
var _b = string_hexadecimal(string_copy(str, 5, 2));
return make_color_rgb(_r, _g, _b);
}
function colorFromHex(hex) {
if(string_length(hex) != 6) return 0;
var rr = string_hexadecimal(string_copy(hex, 1, 2));
var gg = string_hexadecimal(string_copy(hex, 3, 2));
var bb = string_hexadecimal(string_copy(hex, 5, 2));
return make_color_rgb(rr, gg, bb);
}

View file

@ -86,9 +86,7 @@ event_inherited();
if(isHover) {
if(mouse_press(mb_left, interactable && sFOCUS)) {
palette = array_create(array_length(pal.palette));
for( var j = 0; j < array_length(pal.palette); j++ )
palette[j] = cola(pal.palette[j]);
palette = array_clone(pal.palette);
onApply(palette);
index_selecting = 0;

View file

@ -45,7 +45,7 @@ function Node_Color_adjust(_x, _y, _group = noone) : Node_Processor(_x, _y, _gro
inputs[| 12] = nodeValue("Input Type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_button, [ "Surface", "Color" ]);
inputs[| 13] = nodeValue("Color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 13] = nodeValue("Color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette)
.setVisible(true, true);

View file

@ -3,7 +3,7 @@ function Node_Color_Remove(_x, _y, _group = noone) : Node_Processor(_x, _y, _gro
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Colors", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 1] = nodeValue("Colors", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)

View file

@ -2,10 +2,10 @@ function Node_Color_replace(_x, _y, _group = noone) : Node_Processor(_x, _y, _gr
name = "Replace Palette";
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Palette from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE, "Color to be replaced.")
inputs[| 1] = nodeValue("Palette from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE), "Color to be replaced.")
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Palette to", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE, "Palette to be replaced to.")
inputs[| 2] = nodeValue("Palette to", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE), "Palette to be replaced to.")
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 3] = nodeValue("Threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)

View file

@ -18,7 +18,7 @@ function Node_Dither(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) co
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 1] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Pattern", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)

View file

@ -2,7 +2,7 @@ function Node_Gradient_Palette(_x, _y, _group = noone) : Node_Processor(_x, _y,
name = "Palette to Gradient";
w = 96;
inputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette)
.setVisible(true, true);

View file

@ -26,7 +26,7 @@ function Node_Gradient_Points(_x, _y, _group = noone) : Node_Processor(_x, _y, _
inputs[| 9] = nodeValue("Use palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false );
inputs[| 10] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 10] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 11] = nodeValue("Falloff 1", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 6 )

View file

@ -5,10 +5,10 @@ function Node_Gradient_Replace_Color(_x, _y, _group = noone) : Node_Processor(_x
inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )
.setVisible(true, true);
inputs[| 1] = nodeValue("Color from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 1] = nodeValue("Color from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Color to", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 2] = nodeValue("Color to", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 3] = nodeValue("Threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)

View file

@ -448,9 +448,9 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor {
switch(prop.type) {
case VALUE_TYPE.integer : return prop.unit.mode == VALUE_UNIT.constant? round(_val) : _val;
case VALUE_TYPE.float : return _val;
case VALUE_TYPE.text : return is_string(_val)? _val : string_real(_val);
case VALUE_TYPE.color : return is_real(_val)? cola(_val) : _val;
case VALUE_TYPE.surface : return is_string(_val)? get_asset(_val) : _val;
case VALUE_TYPE.text : return is_string(_val)? _val : string_real(_val);
case VALUE_TYPE.color : return is_real(_val)? cola(_val) : _val;
case VALUE_TYPE.surface : return is_string(_val)? get_asset(_val) : _val;
}
return _val;

View file

@ -2,7 +2,7 @@ function Node_Palette(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c
name = "Palette";
setDimension(96);
inputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE)
inputs[| 0] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 1] = nodeValue("Trim range", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 1 ])

View file

@ -2,14 +2,14 @@ function Node_Palette_Replace(_x, _y, _group = noone) : Node_Processor(_x, _y, _
name = "Palette Replace";
setDimension(96);
inputs[| 0] = nodeValue("Palette in", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 0] = nodeValue("Palette in", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette)
.setVisible(true, true);
inputs[| 1] = nodeValue("Palette from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 1] = nodeValue("Palette from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Palette to", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 2] = nodeValue("Palette to", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 3] = nodeValue("Threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)

View file

@ -3,7 +3,7 @@ function Node_Palette_Shift(_x, _y, _group = noone) : Node_Processor(_x, _y, _gr
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE)
inputs[| 1] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Shift", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)

View file

@ -2,7 +2,7 @@ function Node_Palette_Sort(_x, _y, _group = noone) : Node_Processor(_x, _y, _gro
name = "Sort Palette";
setDimension(96);
inputs[| 0] = nodeValue("Palette in", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 0] = nodeValue("Palette in", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette)
.setVisible(true, true);

View file

@ -3,7 +3,7 @@ function Node_Posterize(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 1] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 1] = nodeValue("Palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 2] = nodeValue("Use palette", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true);

View file

@ -5,7 +5,7 @@ function Node_Region_Fill(_x, _y, _group = noone) : Node_Processor(_x, _y, _grou
inputs[| 1] = nodeValue("Mask", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, noone);
inputs[| 2] = nodeValue("Fill Colors", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 2] = nodeValue("Fill Colors", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
inputs[| 3] = nodeValue("Fill", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true);

View file

@ -1,7 +1,7 @@
function Node_Surface_From_Color(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Surface from Color";
inputs[| 0] = nodeValue("Color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE )
inputs[| 0] = nodeValue("Color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE))
.setDisplay(VALUE_DISPLAY.palette);
outputs[| 0] = nodeValue("Surface", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);

View file

@ -587,9 +587,9 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
if(_type == VALUE_TYPE.color) {
if(is_array(_value)) {
for( var i = 0, n = array_length(_value); i < n; i++ )
_value[i] = int64(cola(_value[i]));
_value[i] = cola(_value[i], _color_get_alpha(_value[i]));
} else
_value = int64(cola(_value));
_value = cola(_value, _color_get_alpha(_value));
}
key_inter = CURVE_TYPE.linear;

View file

@ -26,7 +26,7 @@ function Node_Widget_Test(_x, _y, _group = noone) : Node(_x, _y, _group) constru
inputs[| 19] = nodeValue("checkBox", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false) .setDisplay(VALUE_DISPLAY._default)
inputs[| 20] = nodeValue("buttonColor", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, 0) .setDisplay(VALUE_DISPLAY._default)
inputs[| 21] = nodeValue("buttonPalette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, DEF_PALETTE) .setDisplay(VALUE_DISPLAY.palette)
inputs[| 21] = nodeValue("buttonPalette", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, array_clone(DEF_PALETTE)) .setDisplay(VALUE_DISPLAY.palette)
inputs[| 22] = nodeValue("buttonGradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white)) .setDisplay(VALUE_DISPLAY._default)
inputs[| 23] = nodeValue("pathArrayBox", self, JUNCTION_CONNECT.input, VALUE_TYPE.path, []) .setDisplay(VALUE_DISPLAY.path_array, { filter: [ "image|*.png;*.jpg", "" ] })

View file

@ -25,6 +25,8 @@ function string_decimal(str) {
function toNumber(str) {
INLINE
if(is_numeric(str)) return str;
try { return real(str); }
catch(e) {}

View file

@ -6,7 +6,7 @@ function string_hexadecimal(str) {
var v = 0;
while(i > 0) {
var ch = string_char_at(str, i);
var ch = string_char_at(str, i);
var val = string_pos(string_upper(ch), HEX) - 1;
v += val * d;