#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(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(("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(); 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(); 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(); //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(); 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("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(); 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& 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("tip-change-thumb"); m_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx)); m_brush_button = find("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(); 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("dual-change-thumb"); m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx)); m_dual_brush_button = find("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(); 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("pattern-change-thumb"); m_pattern_thumb->set_image(m_pattern_popup->get_thumb_path(0)); m_pattern_button = find("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(); 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("canvas"); m_preview->m_draw_first = true; m_blend_mode = find("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("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("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("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("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("brush-settings-reset"); m_brush_settings_reset->on_click = [br_idx,this](Node*) { auto b = std::make_shared(); 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(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(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; }