Files
panopainter/src/node_panel_stroke.cpp

690 lines
28 KiB
C++

#include "pch.h"
#include "log.h"
#include "node_panel_stroke.h"
#include "canvas.h"
#include "node_button.h"
#include "app.h"
#include "abr.h"
Node* NodePanelStroke::clone_instantiate() const
{
return new NodePanelStroke();
}
void NodePanelStroke::clone_finalize(Node* dest) const
{
NodePanelStroke* n = static_cast<NodePanelStroke*>(dest);
n->init_controls();
}
void NodePanelStroke::init()
{
init_template("tpl-panel-stroke");
init_controls();
}
bool NodePanelStroke::import_abr(const std::string& path)
{
BT_SetTerminate();
ABR abr;
LOG("ABR detected");
std::string name, base, ext;
std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
std::smatch m;
if (!std::regex_search(path, m, r))
return false;
base = m[1].str();
name = m[2].str();
ext = m[3].str();
if (!str_iequals(ext, "abr") || !Asset::exist(path))
return false;
auto pb = App::I->show_progress("Importing ABR");
abr.open(path);
int tot = (int)(abr.m_samples.size() + abr.m_patterns.size() + abr.m_presets.size());
std::atomic_int count(0);
parallel_for(abr.m_samples.size(), [&](size_t i)
//for (const auto& samp : abr.m_samples)
{
auto ii = abr.m_samples.begin();
std::advance(ii, i);
const auto& samp = *ii;
std::string path_high = App::I->data_path + "/brushes/" + samp.first + ".png";
std::string path_thumb = App::I->data_path + "/brushes/thumbs/" + samp.first + ".png";
auto padded = samp.second->resize_squared(glm::u8vec4(255));
//auto high = padded.resize_power2();
//high.save(path_high);
samp.second->save(path_high);
auto thumb = padded.resize(64, 64);
thumb.save(path_thumb);
NodeButtonBrush* brush = new NodeButtonBrush;
m_brush_popup->m_container->add_child(brush);
brush->init();
brush->create();
brush->loaded();
brush->set_icon(path_thumb.c_str());
brush->thumb_path = path_thumb;
brush->high_path = path_high;
brush->brush_name = name;
brush->m_user_brush = true;
brush->on_click = std::bind(&NodePanelBrush::handle_click, m_brush_popup, std::placeholders::_1);
count++;
float prog = (float)count / (float)tot;
pb->m_progress->SetWidthP(prog * 100.f);
});
m_brush_popup->save();
parallel_for(abr.m_patterns.size(), [&](size_t i)
//for (const auto& patt : abr.m_patterns)
{
auto ii = abr.m_patterns.begin();
std::advance(ii, i);
const auto& patt = *ii;
std::string path_high = App::I->data_path + "/patterns/" + patt.first + ".png";
std::string path_thumb = App::I->data_path + "/patterns/thumbs/" + patt.first + ".png";
patt.second->save(path_high);
auto thumb = patt.second->resize(64, 64);
thumb.save(path_thumb);
NodeButtonBrush* brush = new NodeButtonBrush;
m_pattern_popup->m_container->add_child(brush);
brush->init();
brush->create();
brush->loaded();
brush->set_icon(path_thumb.c_str());
brush->thumb_path = path_thumb;
brush->high_path = path_high;
brush->brush_name = name;
brush->m_user_brush = true;
brush->on_click = std::bind(&NodePanelBrush::handle_click, m_pattern_popup, std::placeholders::_1);
count++;
float prog = (float)count / (float)tot;
pb->m_progress->SetWidthP(prog * 100.f);
});
m_pattern_popup->save();
auto brushes = abr.compute_brushes(App::I->data_path);
for (const auto& pr : brushes)
{
if (pr->valid())
{
LOG("add preset %s", pr->m_name.c_str());
App::I->presets->add_brush(pr);
}
count++;
float prog = (float)count / (float)tot;
pb->m_progress->SetWidthP(prog * 100.f);
}
App::I->presets->save();
pb->destroy();
//save();
return true;
}
void NodePanelStroke::update_controls()
{
const auto& b = Canvas::I->m_current_brush;
m_tip_size->m_value = m_curves[m_tip_size].to_slider(b->m_tip_size);
m_tip_spacing->m_value = m_curves[m_tip_spacing].to_slider(b->m_tip_spacing);
m_tip_flow->m_value = m_curves[m_tip_flow].to_slider(b->m_tip_flow);
m_tip_opacity->m_value = b->m_tip_opacity;
m_tip_angle->m_value = b->m_tip_angle;
m_tip_angle_smooth->m_value = b->m_tip_angle_smooth;
m_tip_wet->m_value = b->m_tip_wet;
m_tip_noise->m_value = b->m_tip_noise;
m_jitter_scale->m_value = b->m_jitter_scale;
m_jitter_angle->m_value = b->m_jitter_angle;
m_jitter_scatter->m_value = m_curves[m_jitter_scatter].to_slider(b->m_jitter_scatter);;
m_jitter_flow->m_value = b->m_jitter_flow;
m_jitter_opacity->m_value = b->m_jitter_opacity;
m_jitter_hue->m_value = b->m_jitter_hue;
m_jitter_sat->m_value = b->m_jitter_sat;
m_jitter_val->m_value = b->m_jitter_val;
m_jitter_hsv_eachsample->set_value(b->m_jitter_hsv_eachsample);
m_jitter_aspect->m_value = b->m_jitter_aspect;
m_tip_angle_follow->set_value(b->m_tip_angle_follow);
m_tip_angle_init->set_value(b->m_tip_angle_init);
m_tip_flow_pressure->set_value(b->m_tip_flow_pressure);
m_tip_opacity_pressure->set_value(b->m_tip_opacity_pressure);
m_tip_size_pressure->set_value(b->m_tip_size_pressure);
m_jitter_aspect_bothaxis->set_value(b->m_jitter_aspect_bothaxis);
m_tip_invert->set_value(b->m_tip_invert);
m_tip_flipx->set_value(b->m_tip_flipx);
m_tip_flipy->set_value(b->m_tip_flipy);
m_pattern_enabled->set_value(b->m_pattern_enabled);
m_dual_enabled->set_value(b->m_dual_enabled);
m_dual_scatter_bothaxis->set_value(b->m_dual_scatter_bothaxis);
m_dual_invert->set_value(b->m_dual_invert);
m_dual_flipx->set_value(b->m_dual_flipx);
m_dual_flipy->set_value(b->m_dual_flipy);
m_dual_randflip->set_value(b->m_dual_randflip);
m_tip_randflipx->set_value(b->m_tip_randflipx);
m_tip_randflipy->set_value(b->m_tip_randflipy);
m_dual_size->m_value = m_curves[m_dual_size].to_slider(b->m_dual_size);
m_dual_spacing->m_value = m_curves[m_dual_spacing].to_slider(b->m_dual_spacing);
m_dual_flow->m_value = m_curves[m_dual_flow].to_slider(b->m_dual_flow);
m_dual_scatter->m_value = m_curves[m_dual_scatter].to_slider(b->m_dual_scatter);
m_tip_aspect->m_value = b->m_tip_aspect;
m_dual_opacity->m_value = b->m_dual_opacity;
m_dual_rotate->m_value = b->m_dual_rotate;
m_pattern_eachsample->set_value(b->m_pattern_eachsample);
m_pattern_invert->set_value(b->m_pattern_invert);
m_pattern_flipx->set_value(b->m_pattern_flipx);
m_pattern_flipy->set_value(b->m_pattern_flipy);
m_pattern_rand_offset->set_value(b->m_pattern_rand_offset);
m_pattern_scale->m_value = m_curves[m_pattern_scale].to_slider(b->m_pattern_scale);
m_pattern_brightness->m_value = b->m_pattern_brightness;
m_pattern_contrast->m_value = b->m_pattern_contrast;
m_pattern_depth->m_value = b->m_pattern_depth;
m_blend_mode->set_index(b->m_blend_mode);
m_dual_blend_mode->set_index(b->m_dual_blend_mode);
m_pattern_blend_mode->set_index(b->m_pattern_blend_mode);
m_preview->m_brush = b;
m_preview->draw_stroke();
if (m_brush_thumb->m_path != b->m_brush_thumb_path)
m_brush_thumb->set_image(b->m_brush_thumb_path);
if (m_dual_brush_thumb->m_path != b->m_dual_thumb_path)
m_dual_brush_thumb->set_image(b->m_dual_thumb_path);
if (m_pattern_thumb->m_path != b->m_pattern_thumb_path)
m_pattern_thumb->set_image(b->m_pattern_thumb_path);
}
void NodePanelStroke::set_flow(float value, bool normalized, bool propagate)
{
float v = normalized ? value : m_curves[m_tip_flow].to_slider(value);
m_tip_flow->set_value(v, propagate);
}
void NodePanelStroke::set_size(float value, bool normalized, bool propagate)
{
float v = normalized ? value : m_curves[m_tip_size].to_value(value);
m_tip_size->set_value(v, propagate);
}
void NodePanelStroke::init_fold(const std::string& name)
{
if (auto b = find<NodeButton>(("button-unfold-" + name).c_str())) {
b->on_click = [this,name](Node*) {
find(("fold-" + name).c_str())->ToggleVisibility();
};
}
}
void NodePanelStroke::init_controls()
{
m_brush_popup = std::make_shared<NodePanelBrush>();
m_brush_popup->m_manager = m_manager;
m_brush_popup->m_dir_name = "brushes";
m_brush_popup->init();
m_brush_popup->create();
m_brush_popup->loaded();
m_brush_popup->SetPositioning(YGPositionTypeAbsolute);
m_brush_popup->SetSize(300, 400);
m_brush_popup->m_mouse_ignore = false;
m_brush_popup->m_flood_events = true;
m_brush_popup->m_capture_children = false;
m_pattern_popup = std::make_shared<NodePanelBrush>();
m_pattern_popup->m_manager = m_manager;
m_pattern_popup->m_dir_name = "patterns";
m_pattern_popup->init();
m_pattern_popup->create();
m_pattern_popup->loaded();
m_pattern_popup->SetPositioning(YGPositionTypeAbsolute);
m_pattern_popup->SetSize(300, 400);
m_pattern_popup->m_mouse_ignore = false;
m_pattern_popup->m_flood_events = true;
m_pattern_popup->m_capture_children = false;
//m_presets_popup = std::make_shared<NodePanelBrushPreset>();
//m_presets_popup->m_manager = m_manager;
//m_presets_popup->init();
//m_presets_popup->create();
//m_presets_popup->loaded();
//m_presets_popup->SetPositioning(YGPositionTypeAbsolute);
//m_presets_popup->SetSize(YGUndefined, 400);
//m_presets_popup->m_mouse_ignore = false;
//m_presets_popup->m_flood_events = true;
//m_presets_popup->m_capture_children = false;
int br_idx = std::max(m_brush_popup->find_brush("Round-Hard"), 0);
// init main brush
auto b = std::make_shared<Brush>();
b->load_tip(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
//b->load_dual(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
//b->load_pattern(m_pattern_popup->get_texture_path(0), m_pattern_popup->get_thumb_path(0));
b->m_tip_size = 30;
b->m_tip_flow = .9f;
b->m_tip_spacing = .1f;
b->m_tip_opacity = 1.f;
Canvas::I->m_current_brush = b;
// BRUSH PRESETS
m_preset_button = find<NodeButtonCustom>("preset-button");
m_preset_button->on_click = [this](Node*) {
auto screen = root()->m_size;
glm::vec2 pos = m_preset_button->m_pos + glm::vec2(m_preset_button->m_size.x, 0);
root()->add_child(App::I->presets);
auto tick = root()->add_child<NodeImage>();
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(16, 32);
tick->SetPosition(pos.x, pos.y + (m_preset_button->m_size.y - 32) * 0.5f);
tick->set_image("data/ui/popup-tick.png");
float hh = App::I->presets->m_container->m_children.size() > 10 ? App::I->height / App::I->zoom * .75f : 400.f;
App::I->presets->SetHeight(glm::max(hh, 400.f));
root()->update();
if ((pos.y + App::I->presets->m_size.y) > screen.y)
pos.y = screen.y - App::I->presets->m_size.y;
if (pos.y < 0)
pos.y = 0;
App::I->presets->SetPosition(pos.x + 16, pos.y);
App::I->presets->SetPositioning(YGPositionTypeAbsolute);
App::I->presets->m_mouse_ignore = false;
App::I->presets->m_flood_events = true;
App::I->presets->m_capture_children = false;
App::I->presets->mouse_capture();
root()->update();
App::I->presets->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
App::I->presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
// don't change some params
//b->m_tip_size = Canvas::I->m_current_brush->m_tip_size;
auto old_color = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
Canvas::I->m_current_brush->m_tip_color = old_color;
Canvas::I->m_current_brush->load();
m_preview->draw_stroke();
m_brush_thumb->set_image(b->m_brush_thumb_path);
m_dual_brush_thumb->set_image(b->m_dual_thumb_path);
m_pattern_thumb->set_image(b->m_pattern_thumb_path);
update_controls();
if (on_stroke_change)
on_stroke_change(this);
};
};
// BRUSH TIP SHAPE
m_brush_thumb = find<NodeImage>("tip-change-thumb");
m_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx));
m_brush_button = find<NodeButtonCustom>("tip-change");
m_brush_button->on_click = [this](Node*) {
auto screen = root()->m_size;
glm::vec2 pos = m_brush_button->m_pos + glm::vec2(m_brush_button->m_size.x, 0);
root()->add_child(m_brush_popup);
auto tick = root()->add_child<NodeImage>();
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(16, 32);
tick->SetPosition(pos.x, pos.y + (m_brush_button->m_size.y - 32) * 0.5f);
tick->set_image("data/ui/popup-tick.png");
root()->update();
if ((pos.y + m_brush_popup->m_size.y) > screen.y)
pos.y = screen.y - m_brush_popup->m_size.y;
if (pos.y < 0)
pos.y = 0;
m_brush_popup->SetPosition(pos.x + 16, pos.y);
m_brush_popup->mouse_capture();
root()->update();
m_brush_popup->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
m_brush_popup->on_brush_changed = [this](Node*, int index) {
if (on_brush_changed)
on_brush_changed(this, m_brush_popup->get_texture_path(index), m_brush_popup->get_thumb_path(index));
m_brush_thumb->set_image(m_brush_popup->get_thumb_path(index));
//m_brush_popup->mouse_release();
//m_brush_popup->m_parent->remove_child(m_brush_popup.get());
};
};
// DUAL BRUSH TIP SHAPE
m_dual_brush_thumb = find<NodeImage>("dual-change-thumb");
m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx));
m_dual_brush_button = find<NodeButtonCustom>("dual-change");
m_dual_brush_button->on_click = [this](Node*) {
auto screen = root()->m_size;
glm::vec2 pos = m_dual_brush_button->m_pos + glm::vec2(m_dual_brush_button->m_size.x, 0);
root()->add_child(m_brush_popup);
auto tick = root()->add_child<NodeImage>();
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(16, 32);
tick->SetPosition(pos.x, pos.y + (m_dual_brush_button->m_size.y - 32) * 0.5f);
tick->set_image("data/ui/popup-tick.png");
root()->update();
if ((pos.y + m_brush_popup->m_size.y) > screen.y)
pos.y = screen.y - m_brush_popup->m_size.y;
if (pos.y < 0)
pos.y = 0;
m_brush_popup->SetPosition(pos.x + 16, pos.y);
m_brush_popup->mouse_capture();
root()->update();
m_brush_popup->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
m_brush_popup->on_brush_changed = [this](Node*, int index) {
m_dual_enabled->set_value(true, true);
if (on_dual_changed)
on_dual_changed(this, m_brush_popup->get_texture_path(index), m_brush_popup->get_thumb_path(index));
m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(index));
};
};
// PATTERN IMAGE
m_pattern_thumb = find<NodeImage>("pattern-change-thumb");
m_pattern_thumb->set_image(m_pattern_popup->get_thumb_path(0));
m_pattern_button = find<NodeButtonCustom>("pattern-change");
m_pattern_button->on_click = [this](Node*) {
auto screen = root()->m_size;
glm::vec2 pos = m_pattern_button->m_pos + glm::vec2(m_pattern_button->m_size.x, 0);
root()->add_child(m_pattern_popup);
auto tick = root()->add_child<NodeImage>();
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(16, 32);
tick->SetPosition(pos.x, pos.y + (m_pattern_button->m_size.y - 32) * 0.5f);
tick->set_image("data/ui/popup-tick.png");
root()->update();
if ((pos.y + m_pattern_popup->m_size.y) > screen.y)
pos.y = screen.y - m_pattern_popup->m_size.y;
if (pos.y < 0)
pos.y = 0;
m_pattern_popup->SetPosition(pos.x + 16, pos.y);
m_pattern_popup->mouse_capture();
root()->update();
m_pattern_popup->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
m_pattern_popup->on_brush_changed = [this](Node*, int index) {
m_pattern_enabled->set_value(true, true);
if (on_pattern_changed)
on_pattern_changed(this, m_pattern_popup->get_texture_path(index), m_pattern_popup->get_thumb_path(index));
m_pattern_thumb->set_image(m_pattern_popup->get_thumb_path(index));
};
};
m_preview = find<NodeStrokePreview>("canvas");
m_preview->m_draw_first = true;
m_blend_mode = find<NodeComboBox>("blend-mode");
m_blend_mode->on_select = [this](Node*, int index) {
Canvas::I->m_current_brush->m_blend_mode = index;
m_preview->draw_stroke();
if (on_stroke_change)
on_stroke_change(this);
};
init_slider(m_tip_size, "tip-size", &Brush::m_tip_size);
init_slider(m_tip_spacing, "tip-spacing", &Brush::m_tip_spacing);
init_slider(m_tip_flow, "tip-flow", &Brush::m_tip_flow);
init_slider(m_tip_opacity, "tip-opacity", &Brush::m_tip_opacity);
init_slider(m_tip_angle, "tip-angle", &Brush::m_tip_angle);
init_slider(m_tip_angle_smooth, "tip-angle-smooth", &Brush::m_tip_angle_smooth);
init_slider(m_tip_mix, "tip-mix", &Brush::m_tip_mix);
init_slider(m_tip_wet, "tip-wet", &Brush::m_tip_wet);
init_slider(m_tip_noise, "tip-noise", &Brush::m_tip_noise);
init_slider(m_tip_hue, "tip-hue", &Brush::m_tip_hue);
init_slider(m_tip_sat, "tip-sat", &Brush::m_tip_sat);
init_slider(m_tip_val, "tip-val", &Brush::m_tip_val);
init_slider(m_jitter_scale, "jitter-scale", &Brush::m_jitter_scale);
init_slider(m_jitter_angle, "jitter-angle", &Brush::m_jitter_angle);
init_slider(m_jitter_scatter, "jitter-scatter", &Brush::m_jitter_scatter);
init_slider(m_jitter_flow, "jitter-flow", &Brush::m_jitter_flow);
init_slider(m_jitter_opacity, "jitter-opacity", &Brush::m_jitter_opacity);
init_slider(m_jitter_hue, "jitter-hue", &Brush::m_jitter_hue);
init_slider(m_jitter_sat, "jitter-sat", &Brush::m_jitter_sat);
init_slider(m_jitter_val, "jitter-val", &Brush::m_jitter_val);
init_slider(m_jitter_aspect, "jitter-aspect", &Brush::m_jitter_aspect);
init_checkbox(m_tip_angle_init, "tip-angle-init", &Brush::m_tip_angle_init);
init_checkbox(m_tip_angle_follow, "tip-angle-follow", &Brush::m_tip_angle_follow);
init_checkbox(m_tip_flow_pressure, "tip-flow-pressure", &Brush::m_tip_flow_pressure);
init_checkbox(m_tip_opacity_pressure, "tip-opacity-pressure", &Brush::m_tip_opacity_pressure);
init_checkbox(m_tip_size_pressure, "tip-size-pressure", &Brush::m_tip_size_pressure);
init_checkbox(m_jitter_scatter_bothaxis, "jitter-scatter-bothaxis", &Brush::m_jitter_scatter_bothaxis);
init_checkbox(m_jitter_aspect_bothaxis, "jitter-aspect-bothaxis", &Brush::m_jitter_aspect_bothaxis);
init_checkbox(m_jitter_hsv_eachsample, "jitter-hsv-eachsample", &Brush::m_jitter_hsv_eachsample);
init_checkbox(m_tip_invert, "tip-invert", &Brush::m_tip_invert);
init_checkbox(m_tip_flipx, "tip-flipx", &Brush::m_tip_flipx);
init_checkbox(m_tip_flipy, "tip-flipy", &Brush::m_tip_flipy);
init_checkbox(m_pattern_enabled, "pattern-enabled", &Brush::m_pattern_enabled);
init_checkbox(m_dual_enabled, "dual-enabled", &Brush::m_dual_enabled);
init_checkbox(m_dual_scatter_bothaxis, "dual-scatter-bothaxis", &Brush::m_dual_scatter_bothaxis);
init_checkbox(m_dual_invert, "dual-invert", &Brush::m_dual_invert);
init_checkbox(m_dual_flipx, "dual-flipx", &Brush::m_dual_flipx);
init_checkbox(m_dual_flipy, "dual-flipy", &Brush::m_dual_flipy);
init_checkbox(m_dual_randflip, "dual-randflip", &Brush::m_dual_randflip);
init_checkbox(m_tip_randflipx, "tip-randflipx", &Brush::m_tip_randflipx);
init_checkbox(m_tip_randflipy, "tip-randflipy", &Brush::m_tip_randflipy);
init_checkbox(m_pattern_eachsample, "pattern-eachsample", &Brush::m_pattern_eachsample);
init_checkbox(m_pattern_invert, "pattern-invert", &Brush::m_pattern_invert);
init_checkbox(m_pattern_flipx, "pattern-flipx", &Brush::m_pattern_flipx);
init_checkbox(m_pattern_flipy, "pattern-flipy", &Brush::m_pattern_flipy);
init_checkbox(m_pattern_rand_offset, "pattern-rand-offset", &Brush::m_pattern_rand_offset);
init_slider(m_dual_size, "dual-size", &Brush::m_dual_size);
init_slider(m_dual_spacing, "dual-spacing", &Brush::m_dual_spacing);
init_slider(m_dual_scatter, "dual-scatter", &Brush::m_dual_scatter);
init_slider(m_tip_aspect, "tip-aspect", &Brush::m_tip_aspect);
init_slider(m_dual_opacity, "dual-opacity", &Brush::m_dual_opacity);
init_slider(m_dual_flow, "dual-flow", &Brush::m_dual_flow);
init_slider(m_dual_rotate, "dual-rotate", &Brush::m_dual_rotate);
init_slider(m_pattern_scale, "pattern-scale", &Brush::m_pattern_scale);
init_slider(m_pattern_brightness, "pattern-brightness", &Brush::m_pattern_brightness);
init_slider(m_pattern_contrast, "pattern-contrast", &Brush::m_pattern_contrast);
init_slider(m_pattern_depth, "pattern-depth", &Brush::m_pattern_depth);
SliderCurve curve_cubic {
[](float v) { return glm::pow(v, 3.f); },
[](float v) { return glm::pow(v, 1.f / 3.f); },
};
SliderCurve curve_quad {
[](float v) { return glm::pow(v, 2.f); },
[](float v) { return glm::pow(v, 1.f / 2.f); },
};
SliderCurve curve_linear1k_perc {
[](float v) { return v * 10.f; },
[](float v) { return v * 0.1f; },
};
SliderCurve curve_size1k_perc {
[](float v) {
float ret = 0;
if (v > 0.00f) ret += glm::pow(glm::min(1.f, (v - 0.00f) / 0.50f), 2.f) * 1.f;
if (v > 0.50f) ret += glm::min(1.f, (v - 0.50f) / 0.25f) * 2.f;
if (v > 0.75f) ret += glm::min(1.f, (v - 0.75f) / 0.10f) * 4.f;
if (v > 0.85f) ret += glm::min(1.f, (v - 0.85f) / 0.05f) * 5.f;
if (v > 0.90f) ret += glm::min(1.f, (v - 0.90f) / 0.10f) * 10.f;
return glm::max(.01f, ret);
},
[](float v) {
float ret = 0;
if (v > 0.f) ret += glm::pow(glm::min(1.f, (v - 0.f) / 1.f), 1.f / 2.f) * 0.50f;
if (v > 1.f) ret += glm::min(1.f, (v - 1.f) / 2.f) * 0.25f;
if (v > 2.f) ret += glm::min(1.f, (v - 2.f) / 4.f) * 0.10f;
if (v > 4.f) ret += glm::min(1.f, (v - 4.f) / 5.f) * 0.05f;
if (v > 5.f) ret += glm::min(1.f, (v - 5.f) / 10.f) * 0.10f;
return ret;
},
};
SliderCurve curve_size5k_pix {
[](float v) {
float ret = 0;
if (v > 0.00f) ret += glm::pow(glm::min(1.f, (v - 0.00f) / 0.50f), 2.f) * 100.f;
if (v > 0.50f) ret += glm::min(1.f, (v - 0.50f) / 0.25f) * 200.f;
if (v > 0.75f) ret += glm::min(1.f, (v - 0.75f) / 0.10f) * 400.f;
if (v > 0.85f) ret += glm::min(1.f, (v - 0.85f) / 0.05f) * 1000.f;
if (v > 0.90f) ret += glm::min(1.f, (v - 0.90f) / 0.10f) * 5000.f;
return glm::max(1.f, ret);
},
[](float v) {
float ret = 0;
if (v > 0.f) ret += glm::pow(glm::min(1.f, (v - 0.f) / 100.f), 1.f / 2.f) * 0.50f;
if (v > 100.f) ret += glm::min(1.f, (v - 100.f) / 200.f) * 0.25f;
if (v > 200.f) ret += glm::min(1.f, (v - 200.f) / 400.f) * 0.10f;
if (v > 400.f) ret += glm::min(1.f, (v - 400.f) / 1000.f) * 0.05f;
if (v > 1000.f) ret += glm::min(1.f, (v - 1000.f) / 5000.f) * 0.10f;
return ret;
},
};
m_curves[m_tip_size] = curve_size5k_pix;
m_curves[m_tip_spacing] = curve_size1k_perc;
m_curves[m_tip_flow] = curve_quad;
m_curves[m_dual_size] = curve_size1k_perc;
m_curves[m_dual_spacing] = curve_size1k_perc;
m_curves[m_dual_scatter] = curve_linear1k_perc;
m_curves[m_dual_flow] = curve_quad;
m_curves[m_pattern_scale] = curve_size1k_perc;
m_curves[m_jitter_scatter] = curve_linear1k_perc;
m_tip_aspect_reset = find<NodeButtonCustom>("tip-aspect-reset");
m_tip_aspect_reset->on_click = [this](Node*) {
m_tip_aspect->set_value(0.5);
Canvas::I->m_current_brush->m_tip_aspect = 0.5f;
m_preview->draw_stroke();
if (on_stroke_change)
on_stroke_change(this);
};
m_dual_blend_mode = find<NodeComboBox>("dual-blend-mode");
m_dual_blend_mode->on_select = [this](Node*, int index) {
Canvas::I->m_current_brush->m_dual_blend_mode = index;
m_preview->draw_stroke();
if (on_stroke_change)
on_stroke_change(this);
};
m_pattern_blend_mode = find<NodeComboBox>("pattern-blend-mode");
m_pattern_blend_mode->on_select = [this](Node*, int index) {
Canvas::I->m_current_brush->m_pattern_blend_mode = index;
m_preview->draw_stroke();
if (on_stroke_change)
on_stroke_change(this);
};
m_preview->m_brush = Canvas::I->m_current_brush;
m_preview->draw_stroke();
init_fold("color");
init_fold("metrics");
init_fold("pattern");
init_fold("dualbrush");
init_fold("medium");
init_fold("colorvar");
init_fold("jitter");
if (auto b = find<NodeButton>("button-unfold-all")) {
b->on_click = [this](Node*) {
static bool visible = true;
visible = !visible;
find("fold-color")->SetVisibility(visible);
find("fold-metrics")->SetVisibility(visible);
find("fold-pattern")->SetVisibility(visible);
find("fold-dualbrush")->SetVisibility(visible);
find("fold-medium")->SetVisibility(visible);
find("fold-colorvar")->SetVisibility(visible);
find("fold-jitter")->SetVisibility(visible);
};
}
m_brush_settings_reset = find<NodeButton>("brush-settings-reset");
m_brush_settings_reset->on_click = [br_idx,this](Node*) {
auto b = std::make_shared<Brush>();
b->load_tip(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
//b->load_dual(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
//b->load_pattern(m_pattern_popup->get_texture_path(0), m_pattern_popup->get_thumb_path(0));
b->m_tip_size = 30;
b->m_tip_flow = .9f;
b->m_tip_spacing = .1f;
b->m_tip_opacity = 1.f;
Canvas::I->m_current_brush = b;
update_controls();
App::I->brush_update(true, true);
};
update_controls();
}
void NodePanelStroke::init_slider(NodeSliderH*& target, const char* id, float Brush::* prop)
{
target = find<NodeSliderH>(id);
target->on_value_changed = std::bind(&NodePanelStroke::handle_slide,
this, prop, std::placeholders::_1, std::placeholders::_2);
//m_canvas->m_brush->*prop = target->m_values;
}
void NodePanelStroke::handle_slide(float Brush::* prop, Node* target, float value)
{
auto curve = m_curves.find((NodeSliderH*)target);
Canvas::I->m_current_brush.get()->*prop = curve != m_curves.end() ? curve->second.to_value(value) : value;
m_preview->draw_stroke();
if (on_stroke_change)
on_stroke_change(this);
}
void NodePanelStroke::init_checkbox(NodeCheckBox*& target, const char* id, bool Brush::* prop)
{
target = find<NodeCheckBox>(id);
target->on_value_changed = std::bind(&NodePanelStroke::handle_checkbox,
this, prop, std::placeholders::_1, std::placeholders::_2);
Canvas::I->m_current_brush.get()->*prop = target->checked;
}
void NodePanelStroke::handle_checkbox(bool Brush::* prop, Node *target, bool value)
{
Canvas::I->m_current_brush.get()->*prop = value;
m_preview->draw_stroke();
if (on_stroke_change)
on_stroke_change(this);
}
kEventResult NodePanelStroke::handle_event(Event* e)
{
switch (e->m_type)
{
case kEventType::MouseUpL:
if (!m_mouse_inside)
{
mouse_release();
m_parent->remove_child(this);
if (on_popup_close)
on_popup_close(this);
}
break;
default:
return kEventResult::Available;
break;
}
return kEventResult::Available;
}