#include "pch.h" #include "log.h" #include "canvas_modes.h" #include "layout.h" #include "canvas.h" #include "shader.h" #include "node_canvas.h" #include "app.h" #include "util.h" NodeCanvas* CanvasMode::node; void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { switch (me->m_type) { case kEventType::MouseDownL: // if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) // { // m_draggingR = true; // m_dragR_start = me->m_pos; // m_pan_start = Canvas::I->m_pan; // node->mouse_capture(); // } break; case kEventType::MouseUpL: // if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) // { // m_draggingR = false; // node->mouse_release(); // } break; case kEventType::MouseDownR: if (App::I->keys[(int)kKey::KeyAlt]) break; m_zooming = App::I->keys[(int)kKey::KeyCtrl]; m_draggingR = true; m_dragR_start = me->m_pos; m_pan_start = Canvas::I->m_pan; m_fov_start = Canvas::I->m_cam_fov; node->mouse_capture(); break; case kEventType::MouseUpR: m_draggingR = false; node->mouse_release(); break; case kEventType::MouseMove: if (m_draggingR) { if (m_zooming) { Canvas::I->m_cam_fov = glm::clamp(m_fov_start - (me->m_pos.x - m_dragR_start.x) * 0.05f, Canvas::I->m_cam_fov_min, Canvas::I->m_cam_fov_max); } else { auto dir = (App::I->has_vr && App::I->vr_active) ? glm::vec2(1, 1) : glm::vec2(-1, -1); Canvas::I->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * dir * (Canvas::I->m_cam_fov / 85.f); auto angle = Canvas::I->m_pan * 0.003f; Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x); } } break; case kEventType::MouseScroll: m_zoom_canvas += me->m_scroll_delta * 0.1f; Canvas::I->m_cam_fov = glm::clamp(Canvas::I->m_cam_fov - me->m_scroll_delta * 2.0f, Canvas::I->m_cam_fov_min, Canvas::I->m_cam_fov_max); App::I->brush_update(); break; case kEventType::MouseCancel: m_draggingR = false; node->mouse_release(); break; default: break; } } void CanvasModeBasicCamera::on_GestureEvent(GestureEvent* ge) { switch (ge->m_type) { case kEventType::GestureStart: m_pan_start = Canvas::I->m_pan; m_zoom_start = m_zoom_canvas; m_camera_fov = Canvas::I->m_cam_fov; break; case kEventType::GestureMove: { Canvas::I->m_pan = m_pan_start + ge->m_pos_delta * glm::vec2(-1, -1) * 0.3f * (Canvas::I->m_cam_fov / 85.f); Canvas::I->m_cam_fov = glm::clamp(m_camera_fov - ge->m_distance_delta * .05f, Canvas::I->m_cam_fov_min, Canvas::I->m_cam_fov_max); auto angle = Canvas::I->m_pan * 0.003f; Canvas::I->m_cam_rot = glm::eulerAngleXY(angle.y, angle.x); App::I->brush_update(); break; } default: break; } } //////////////////////////////////////////////////////////////////// void CanvasModePen::on_GestureEvent(GestureEvent* ge) { m_draw_tip = false; } void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { #if defined(__IOS__) m_draw_tip = (me->m_source == kEventSource::Mouse && me->m_type != kEventType::MouseUpL); #else m_draw_tip = (me->m_source == kEventSource::Mouse || me->m_source == kEventSource::Stylus); #endif m_draw_outline = true; if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; #if _WIN32 // curve https://www.wolframalpha.com/input/?i=plot+(1-(x-1)%5E2)%5E2+from+x%3D0+to+1 auto curve = [](float x, float max) { return x > max ? 1.f : glm::pow(1.f - glm::pow(x / max - 1.f, 2.f), 2.f); }; me->m_pressure = curve(me->m_pressure, 0.95f); #endif switch (me->m_type) { case kEventType::MouseDownL: if (App::I->keys[(int)kKey::KeyAlt] || m_picking) { m_picking = true; Canvas::I->pick_start(); glm::vec4 pix = Canvas::I->pick_get(loc); Canvas::I->m_current_brush->m_tip_color = pix; App::I->brush_update(); } else { App::I->render_task_async([loc, pr=me->m_pressure] { Canvas::I->stroke_start({ loc, 0 }, pr); }); } m_dragging = true; node->mouse_capture(); break; case kEventType::MouseUpL: if (m_dragging && !m_picking) { node->mouse_release(); App::I->render_task_async([] { Canvas::I->stroke_end(); }); } if (m_dragging && m_picking) { node->mouse_release(); glm::vec4 pix = Canvas::I->pick_get(loc); Canvas::I->m_current_brush->m_tip_color = pix; App::I->brush_update(); Canvas::I->pick_end(); } m_dragging = false; m_picking = false; break; case kEventType::MouseDownR: if (App::I->keys[(int)kKey::KeyAlt]) { m_resizing = true; m_dragging = true; m_size_pos_start = m_cur_pos; auto curve = App::I->stroke->m_curves[App::I->stroke->m_tip_size]; m_size_value_start = curve.to_slider(Canvas::I->m_current_brush->m_tip_size); node->mouse_capture(); } break; case kEventType::MouseUpR: if (m_dragging && m_resizing) { node->mouse_release(); m_dragging = false; m_resizing = false; } break; case kEventType::MouseMove: if (m_dragging && !m_picking && !m_resizing) { App::I->render_task_async([loc, pr=me->m_pressure] { Canvas::I->stroke_update({ loc, 0 }, pr); }); } if (m_dragging && m_picking) { glm::vec4 pix = Canvas::I->pick_get(loc); Canvas::I->m_current_brush->m_tip_color = pix; App::I->brush_update(); } if (m_dragging && m_resizing) { auto diff = m_cur_pos - m_size_pos_start; auto curve = App::I->stroke->m_curves[App::I->stroke->m_tip_size]; Canvas::I->m_current_brush->m_tip_size = glm::max(curve.to_value(m_size_value_start + diff.x * 0.001f), 0.001f); App::I->brush_update(); } m_cur_pos = loc; break; case kEventType::MouseCancel: if (m_dragging) { App::I->render_task_async([] { Canvas::I->stroke_cancel(); }); m_dragging = false; node->mouse_release(); } if (m_picking) m_picking = false; if (m_resizing) m_resizing = false; break; default: break; } } void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { if (m_draw_tip) { const auto& brush = Canvas::I->m_current_brush; auto pos = m_resizing ? m_size_pos_start : m_cur_pos; //if (App::I->keys[(int)kKey::KeyAlt] && !m_resizing) // pos.x = pos.x - brush->m_tip_size * .5f; ShaderManager::use(kShader::StrokePreview); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_float(kShaderUniform::Alpha, brush->m_tip_flow * brush->m_tip_opacity); float tip_scale_fix = 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f)); float tip_angle = brush->m_tip_angle * (float)(M_PI * 2.0); glm::vec2 tip_scale = App::I->zoom * brush->m_tip_scale * glm::vec2(brush->m_tip_size * tip_scale_fix) * glm::vec2(brush->m_tip_flipx ? -1 : 1, brush->m_tip_flipy ? -1.f : 1.f) * glm::vec2((brush->m_tip_aspect <= 0.5 ? brush->m_tip_aspect * 2.f : 1.f), (brush->m_tip_aspect > 0.5 ? 1.f - (brush->m_tip_aspect - .5f) * 2.f : 1.f)); glm::vec2 tip_offset = glm::vec2(0); auto tip_color = glm::vec4(glm::vec3(brush->m_tip_color), 1.f); if (Canvas::I->m_current_stroke) { const auto& s = Canvas::I->m_current_stroke->m_prev_sample; if (s.size > 0.f) { tip_scale = App::I->zoom * (brush->m_tip_size * tip_scale_fix) * s.scale; tip_angle = s.angle; tip_offset = s.pos - s.origin; tip_color = glm::vec4(s.col, s.flow); } } glm::u8vec4 pixel; glReadPixels(pos.x / App::I->zoom, (App::I->height - pos.y - 1) / App::I->zoom, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); bool outline = glm::min(tip_scale.x, tip_scale.y) < 20 || m_resizing ? false : m_draw_outline; ShaderManager::u_int(kShaderUniform::DrawOutline, outline); ShaderManager::u_vec4(kShaderUniform::Col, outline ? glm::vec4(1.f - glm::vec3(pixel) / 255.f, 1.f) : tip_color); ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(1, -1, 1)) * ortho * glm::translate(glm::vec3(pos + tip_offset, 0)) * glm::eulerAngleZ(tip_angle) * glm::scale(glm::vec3(tip_scale, 1)) ); bool blend = glIsEnabled(GL_BLEND); glEnable(GL_BLEND); glActiveTexture(GL_TEXTURE0); auto& tex = *brush->m_tip_texture; tex.bind(); Canvas::I->m_sampler_brush.bind(0); Canvas::I->m_plane.draw_fill(); tex.unbind(); if (!blend) glDisable(GL_BLEND); } } void CanvasModePen::leave(kCanvasMode next) { } void CanvasModePen::enter(kCanvasMode prev) { m_cur_pos = Canvas::I->m_cur_pos; } //////////////////////////////////////////////////////////////////// void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { #if defined(__IOS__) m_draw_tip = (me->m_source == kEventSource::Mouse && me->m_type != kEventType::MouseUpL); #else m_draw_tip = (me->m_source == kEventSource::Mouse || me->m_source == kEventSource::Stylus); #endif if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; switch (me->m_type) { case kEventType::MouseDownL: node->mouse_capture(); m_dragging = true; m_drag_start = loc; m_drag_pos = loc; break; case kEventType::MouseUpL: node->mouse_release(); if (m_dragging) { App::I->render_task_async([=] { Canvas::I->stroke_start({ m_drag_start, 0 }, 1.f); Canvas::I->stroke_update({ m_drag_pos, 0 }, 1.f); Canvas::I->stroke_draw(); Canvas::I->stroke_end(); }); } m_dragging = false; break; case kEventType::MouseMove: if (m_dragging) m_drag_pos = loc; m_cur_pos = loc; break; case kEventType::MouseCancel: node->mouse_release(); m_dragging = false; break; default: break; } } void CanvasModeLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { if (m_dragging) { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, ortho); ShaderManager::u_vec4(kShaderUniform::Col, Canvas::I->m_current_brush->m_tip_color); static glm::vec4 AB[2]; AB[0] = { m_drag_start, 0, 1 }; AB[1] = { m_drag_pos, 0, 1 }; AB[0].y = Canvas::I->m_box.w - AB[0].y - 1; // invert Y AB[1].y = Canvas::I->m_box.w - AB[1].y - 1; // invert Y m_line.update_vertices(AB); m_line.draw_stroke(); } else if (m_draw_tip) { const auto& brush = Canvas::I->m_current_brush; ShaderManager::use(kShader::StrokePreview); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_float(kShaderUniform::Alpha, brush->m_tip_flow * brush->m_tip_opacity); float tip_scale_fix = 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f)); float tip_angle = brush->m_tip_angle * (float)(M_PI * 2.0); glm::vec2 tip_scale = App::I->zoom * brush->m_tip_scale * glm::vec2(brush->m_tip_size * tip_scale_fix) * glm::vec2(brush->m_tip_flipx ? -1 : 1, brush->m_tip_flipy ? -1.f : 1.f) * glm::vec2((brush->m_tip_aspect <= 0.5 ? brush->m_tip_aspect * 2.f : 1.f), (brush->m_tip_aspect > 0.5 ? 1.f - (brush->m_tip_aspect - .5f) * 2.f : 1.f)); auto tip_color = glm::vec4(glm::vec3(brush->m_tip_color), 1.f); ShaderManager::u_int(kShaderUniform::DrawOutline, false); ShaderManager::u_vec4(kShaderUniform::Col, tip_color); ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(1, -1, 1)) * ortho * glm::translate(glm::vec3(m_cur_pos, 0)) * glm::eulerAngleZ(tip_angle) * glm::scale(glm::vec3(tip_scale, 1)) ); bool blend = glIsEnabled(GL_BLEND); glEnable(GL_BLEND); glActiveTexture(GL_TEXTURE0); auto& tex = *brush->m_tip_texture; tex.bind(); Canvas::I->m_sampler_brush.bind(0); Canvas::I->m_plane.draw_fill(); tex.unbind(); if (!blend) glDisable(GL_BLEND); } } void CanvasModeLine::init() { m_line.create(); } void CanvasModeLine::enter(kCanvasMode prev) { m_cur_pos = Canvas::I->m_cur_pos; } void CanvasModeLine::leave(kCanvasMode next) { } //////////////////////////////////////////////////////////////////// void CanvasModeCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; switch (me->m_type) { case kEventType::MouseDownR: Canvas::I->m_cam_pos = { 0, 0, 0 }; break; case kEventType::MouseDownL: m_dragging = true; m_drag_start = me->m_pos; m_pos_start = xy(Canvas::I->m_cam_pos); node->mouse_capture(); break; case kEventType::MouseUpL: m_dragging = false; node->mouse_release(); Canvas::I->m_cam_pos = { 0, 0, 0 }; break; case kEventType::MouseMove: if (m_dragging) Canvas::I->m_cam_pos = glm::vec3(m_pos_start + (me->m_pos - m_drag_start) * glm::vec2(1, -1) * 0.001f, Canvas::I->m_cam_pos.z); break; case kEventType::MouseCancel: m_dragging = false; node->mouse_release(); break; default: break; } } //////////////////////////////////////////////////////////////////// void CanvasModeGrid::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; switch (me->m_type) { case kEventType::MouseDownL: { node->mouse_capture(); glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 fb_pos; if (Canvas::I->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, m_plane_id)) { m_action = std::make_unique(); m_action->m_mode = this; m_action->m_highlight = m_highlight; m_action->m_selected_index = m_selected_index; m_action->m_lines = m_lines; int select = -1; for (int i = 0; i < m_lines.size(); i++) { auto const& l = m_lines[i]; float d = lines_distance(ro, ro+rd*10.f, l.o-l.d*10.f, l.o+l.d*10.f); if (d < 0.03f) select = i; } if (select == -1) { m_lines.push_back({ hit_o, hit_d }); m_selected_index = (int)m_lines.size() - 1; m_highlight = false; m_added = true; } else { m_selected_index = select; m_highlight = true; m_added = false; } origin = hit_o; dir = hit_d; m_dragging = true; } break; } case kEventType::MouseUpL: node->mouse_release(); m_dragging = false; ActionManager::add(m_action.release()); //commit(); break; case kEventType::MouseMove: { glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 hit_fb; if (m_dragging && Canvas::I->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, m_plane_id)) { m_lines[m_selected_index] = { hit_o, hit_d }; origin = hit_o; dir = hit_d; m_dragging = true; } break; } case kEventType::MouseCancel: if (m_dragging && m_selected_index == m_lines.size() - 1) m_lines.pop_back(); m_dragging = false; node->mouse_release(); break; default: break; } } void CanvasModeGrid::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { const glm::vec4 blue(0, 0, 1, 1); const glm::vec4 red(1, 0, 0, 1); for (int i = 0; i < m_lines.size(); i++) { auto const& l = m_lines[i]; auto origin = l.o; auto dir = l.d; ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, m_highlight && i == m_selected_index ? blue : red); static glm::vec4 AB[2]; AB[0] = {origin - dir * 10.f, 1}; AB[1] = {origin + dir * 10.f, 1 }; m_line.update_vertices(AB); m_line.draw_stroke(); } } void CanvasModeGrid::init() { m_line.create(); } void CanvasModeGrid::commit() { auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj){ ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, {1, 0, 0, 1}); static glm::vec4 AB[2]; AB[0] = {origin - dir * 10.f, 1}; AB[1] = {origin + dir * 10.f, 1 }; m_line.update_vertices(AB); m_line.draw_stroke(); }; Canvas::I->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2)); } void CanvasModeGrid::clear() { auto a = new ActionModeGrid; a->m_mode = this; a->m_highlight = m_highlight; a->m_selected_index = m_selected_index; a->m_lines = m_lines; ActionManager::add(a); m_lines.clear(); } void CanvasModeGrid::leave(kCanvasMode next) { m_selected_index = -1; } void CanvasModeGrid::on_KeyEvent(KeyEvent* ke) { if ((ke->m_key == kKey::KeyBackspace || ke->m_key == kKey::KeyDel) && ke->m_type == kEventType::KeyUp) { if (m_highlight) { auto a = new ActionModeGrid; a->m_mode = this; a->m_highlight = m_highlight; a->m_selected_index = m_selected_index; a->m_lines = m_lines; ActionManager::add(a); m_lines.erase(m_lines.begin() + m_selected_index); m_highlight = false; } } } Action* ActionModeGrid::get_redo() { auto a = new ActionModeGrid; a->m_mode = m_mode; a->m_highlight = m_mode->m_highlight; a->m_selected_index = m_mode->m_selected_index; a->m_lines = m_mode->m_lines; return a; } void ActionModeGrid::undo() { m_mode->m_highlight = m_highlight; m_mode->m_selected_index = m_selected_index; m_mode->m_lines = m_lines; } //////////////////////////////////////////////////////////////////// void CanvasModeMaskFree::init() { m_shape.create(); } void CanvasModeMaskFree::leave(kCanvasMode next) { // Canvas::I->draw_objects(std::bind(&CanvasModeFill::on_Draw, this, glm::mat4(1), std::placeholders::_1, std::placeholders::_2)); if (next != kCanvasMode::Draw && next != kCanvasMode::Erase && next != kCanvasMode::Line) { m_points.clear(); Canvas::I->m_smask_active = false; } } void CanvasModeMaskFree::clear() { m_points.clear(); m_points2d.clear(); m_shape.clear(); } void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { static glm::vec2 oldpos; static glm::vec2 oldvec; static float acc = 0.f; if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; switch (me->m_type) { case kEventType::MouseDownL: { node->mouse_capture(); m_dragging = true; m_points2d.clear(); m_points.clear(); oldpos = loc; oldvec = {1.f, 0.f}; acc = 0; vertex_t vert; vert.pos = glm::vec4(loc, 0, 1); m_points2d.push_back(loc); m_points2d.push_back(loc); m_points.push_back(vert); m_points.push_back(vert); if (!(App::I->keys[(int)kKey::KeyShift] || App::I->keys[(int)kKey::KeyCtrl])) Canvas::I->m_smask.clear({0, 0, 0, 0}); Canvas::I->m_smask_active = true; Canvas::I->m_smask_mode = 1; break; } case kEventType::MouseUpL: node->mouse_release(); m_dragging = false; if (m_points2d.size() > 3) { if (!m_points.empty()) { m_selection_cam = Canvas::I->get_camera(); //m_points2d = poly_intersect(poly_remove_duplicate(m_points2d), Canvas::I->face_to_shape2D(0)); auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) { //glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, App::I->keys[(int)kKey::KeyCtrl] ? glm::vec4(0, 0, 0, 1) : glm::vec4(1, 1, 1, 1)); m_shape.draw_fill(); }; // use m_shape to render the mask polygon auto v = triangulate(poly_remove_duplicate(m_points2d)); Canvas::I->project2Dpoints(v); m_shape.update_vertices(v.data(), (int)v.size()); Canvas::I->draw_objects_direct(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->m_smask); // close the path and reset m_shape to contour rendering m_points.push_back(m_points.back()); m_points.push_back(m_points.front()); Canvas::I->project2Dpoints(m_points); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } } else { Canvas::I->m_smask_active = false; } break; case kEventType::MouseMove: { if (m_dragging) { auto v = loc - oldpos; float len = glm::length(v); if (len > 5) { m_points.back().pos = glm::vec4(loc, 0, 1); m_points2d.back() = loc; v = glm::normalize(v); float d = 1.f - glm::dot(v, oldvec); acc += d; oldpos = loc; oldvec = v; if (acc > 0.001) // angle change tolerance { //LOG("d=%f acc=%f", d, acc); acc = 0; m_points2d.push_back(loc); vertex_t vert; vert.pos = glm::vec4(loc, 0, 1); m_points.push_back(vert); m_points.push_back(vert); } m_shape.update_vertices(m_points.data(), (int)m_points.size()); } } break; } case kEventType::MouseCancel: if (m_dragging) { m_points.pop_back(); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } m_dragging = false; node->mouse_release(); if (m_points.size() < 4) { m_points.clear(); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } break; default: break; } } void CanvasModeMaskFree::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { bool depth = glIsEnabled(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST); if (m_points.size() > 3) { if (m_dragging) { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(1,-1,1)) * ortho); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); //m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill(); m_shape.draw_stroke(); } //else //{ // ShaderManager::use(kShader::Color); // ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); // ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); // m_shape.draw_stroke(); //} } if (depth) glEnable(GL_DEPTH_TEST); } //////////////////////////////////////////////////////////////////// void CanvasModeMaskLine::init() { m_shape.create(); } void CanvasModeMaskLine::leave(kCanvasMode next) { if (next != kCanvasMode::Draw && next != kCanvasMode::Erase && next != kCanvasMode::Line) { m_points.clear(); m_active_tool = false; Canvas::I->m_smask_active = false; } if (!m_active_tool) return; if (m_points2d.size() > 3) { std::vector points; for (int i = 0; i < (int)m_points2d.size(); i++) points.emplace_back(m_points2d[i]); auto v = triangulate(poly_remove_duplicate(points)); Canvas::I->project2Dpoints(v); LOG("%d points", (int)v.size()); m_shape.update_vertices(v.data(), (int)v.size()); if (!m_points.empty()) { auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) { //glEnable(GL_BLEND); ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, {1, 1, 1, 1}); m_shape.draw_fill(); }; Canvas::I->draw_objects_direct(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->m_smask); //m_points.clear(); // close the path m_points.push_back(m_points.back()); m_points.push_back(m_points.back()); m_points.push_back(m_points.front()); Canvas::I->project2Dpoints(m_points); // reset m_shape to contour rendering m_shape.update_vertices(m_points.data(), (int)m_points.size()); } } else { Canvas::I->m_smask_active = false; } m_active_tool = false; } void CanvasModeMaskLine::enter(kCanvasMode prev) { //m_points2d.clear(); //m_points.clear(); //Canvas::I->m_smask.clear({0, 0, 0, 0}); //m_active_tool = true; } void CanvasModeMaskLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; switch (me->m_type) { case kEventType::MouseDownL: { if (m_active_tool == false) { m_active_tool = true; m_points2d.clear(); m_points.clear(); Canvas::I->m_smask.clear({ 0, 0, 0, 0 }); Canvas::I->m_smask_active = true; Canvas::I->m_smask_mode = 2; } node->mouse_capture(); m_dragging = true; m_points2d.push_back(loc); vertex_t vert; vert.pos = glm::vec4(loc, 0, 1); m_points.push_back(vert); m_shape.update_vertices(m_points.data(), (int)m_points.size()); break; } case kEventType::MouseUpL: node->mouse_release(); m_dragging = false; if (m_points.size() > 1) { m_selection_cam = Canvas::I->get_camera(); m_points.push_back(m_points.back()); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } break; case kEventType::MouseMove: { if (m_dragging) { if (glm::distance(m_points2d.front(), loc) < 10) loc = m_points2d.front(); m_points.back().pos = glm::vec4(loc, 0, 1); m_points2d.back() = loc; m_shape.update_vertices(m_points.data(), (int)m_points.size()); } break; } case kEventType::MouseCancel: if (m_dragging) { m_points.pop_back(); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } m_dragging = false; node->mouse_release(); if (m_points.size() < 4) { m_points.clear(); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } break; default: break; } } void CanvasModeMaskLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { if (m_points.size() > 1) { if (m_active_tool) { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(1,-1,1)) * ortho); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); //m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill(); m_shape.draw_stroke(); } else { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); m_shape.draw_stroke(); } } } //////////////////////////////////////////////////////////////////// void CanvasModeFill::init() { m_shape.create(); } void CanvasModeFill::leave(kCanvasMode next) { if (m_points.size() > 2) { auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, {1, 1, 1, 1}); m_shape.draw_fill(); }; Canvas::I->draw_objects_direct(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->m_smask); m_points.clear(); Canvas::I->m_smask_active = true; } else { Canvas::I->m_smask_active = false; } } void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; switch (me->m_type) { case kEventType::MouseDownL: { node->mouse_capture(); m_dragging = true; glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 hit_fb; int plane_id; if (Canvas::I->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0)) { m_dirty_planes[plane_id]++; vertex_t v; v.pos = glm::vec4(hit_o, 1); v.uvs = glm::vec2(0); if (m_points.size() < 3) { m_points.push_back(v); } else { auto last = m_points.back(); m_points.push_back(m_points[0]); m_points.push_back(last); m_points.push_back(v); } m_shape.update_vertices(m_points.data(), (int)m_points.size()); } Canvas::I->m_smask.clear({0, 0, 0, 0}); break; } case kEventType::MouseUpL: node->mouse_release(); m_dragging = false; break; case kEventType::MouseMove: { glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 fb_pos; int plane_id; if (m_dragging && Canvas::I->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, plane_id)) { vertex_t v; v.pos = glm::vec4(hit_o, 1); v.uvs = glm::vec2(0); m_points.back() = v; m_shape.update_vertices(m_points.data(), (int)m_points.size()); } break; } case kEventType::MouseCancel: if (m_dragging) { m_points.pop_back(); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } m_dragging = false; node->mouse_release(); if (m_points.size() < 4) { m_points.clear(); m_shape.update_vertices(m_points.data(), (int)m_points.size()); } break; default: break; } } void CanvasModeFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { if (!m_points.empty()) { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, .25 }); m_dragging ? m_shape.draw_fill() : m_shape.draw_stroke(); } } //////////////////////////////////////////////////////////////////// void CanvasModeTransform::init() { m_sphere.create(1.f, glm::radians(-10.f), glm::radians(10.f), glm::radians(-10.f), glm::radians(10.f), 1.f); m_circle.create<16>(1.f); for (int i = 0; i < 6; i++) { m_shape[i].create(); m_points_face[i].clear(); } m_xform = glm::mat4(1); m_xform_local = glm::mat4(1); } void CanvasModeTransform::enter(kCanvasMode prev) { m_commit_on_leave = false; for (int i = 0; i < 6; i++) { m_shape[i].clear(); m_points_face[i].clear(); } if (m_action == ActionType::Import) { float aspect = 1.f; if (m_source_image.data()) { m_tex[0].create(m_source_image); aspect = (float)m_source_image.width / (float)m_source_image.height; } auto center = zw(Canvas::I->m_box) * 0.5f; glm::vec2 bb_sz = glm::vec2(aspect, 1.f) * 100.f * App::I->zoom; glm::vec2 bb_min = center - bb_sz * 0.5f; glm::vec2 bb_max = center + bb_sz * 0.5f; glm::vec2 midpoint = (bb_min + bb_max) * 0.5f; auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1); auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)); m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up))); m_xform_local = glm::mat4(1); corners.clear(); corners.emplace_back(bb_min, 0); // A corners.emplace_back(bb_max, 0); // C corners.emplace_back(bb_max.x, bb_min.y, 0); // B corners.emplace_back(bb_min.x, bb_max.y, 0); // D corners.emplace_back(midpoint, 0); corners.emplace_back(midpoint + (bb_max - bb_min) * glm::vec2(0.75f, 0), 0); for (auto& c : corners) c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1); m_points_face[0] = std::vector({ vertex_t(corners[0], { 0, 0 }), vertex_t(corners[2], { 1, 0 }), vertex_t(corners[1], { 1, 1 }), vertex_t(corners[3], { 0, 1 }), }); auto shape3d = triangulate(m_points_face[0]); m_shape[0].update_vertices(shape3d.data(), (int)shape3d.size()); m_commit_on_leave = true; return; } // avoid recursive loop, store the last different mode not using Transform static kCanvasMode last_prev = kCanvasMode::Draw; if (prev != kCanvasMode::Copy && prev != kCanvasMode::Cut && prev != kCanvasMode::Import) last_prev = prev; if (prev != kCanvasMode::MaskFree && prev != kCanvasMode::MaskLine) { Canvas::set_mode(last_prev); return; } auto m = static_cast(Canvas::I->modes[(int)prev][0]); if (m->m_points2d.size() < 3) { Canvas::set_mode(last_prev); return; } Canvas::I->m_smask_active = false; auto points = m->m_points2d; Canvas::I->push_camera(); Canvas::I->set_camera(m->m_selection_cam); glm::vec2 bb_min(FLT_MAX); glm::vec2 bb_max(-FLT_MAX); for (auto p2d : points) { bb_min = glm::min(bb_min, p2d); bb_max = glm::max(bb_max, p2d); } glm::vec2 midpoint = (bb_min + bb_max) * 0.5f; auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1); auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)); m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up))); m_xform_local = glm::mat4(1); corners.clear(); corners.emplace_back(bb_min, 0); corners.emplace_back(bb_max, 0); corners.emplace_back(bb_max.x, bb_min.y, 0); corners.emplace_back(bb_min.x, bb_max.y, 0); corners.emplace_back(midpoint, 0); corners.emplace_back(midpoint + (bb_max-bb_min) * glm::vec2(0.75f, 0), 0); for (auto& c : corners) c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1); for (int plane = 0; plane < 6; plane++) { auto face = Canvas::I->face_to_shape2D(plane); auto shape2d = poly_intersect(points, face); if (shape2d.size() < 3 || face.empty()) { m_shape[plane].clear(); m_points_face[plane].clear(); continue; } m_points_face[plane].reserve(shape2d.size()); glm::vec2 bb_min(Canvas::I->m_size); glm::vec2 bb_max(0, 0); for (auto p2d : shape2d) { p2d.y = Canvas::I->m_box.w - p2d.y - 1; auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f); auto p3d_plane = Canvas::I->m_plane_unproject[plane] * glm::vec4(p2d_clip, 0, 1); if (p3d_plane.w < 0) continue; auto p3d_norm = -p3d_plane / p3d_plane.z; auto p2d_plane = xy(p3d_norm); auto p2d_plane_raster = (p2d_plane * 0.5f + 0.5f) * Canvas::I->m_size; auto p3d_world = Canvas::I->m_plane_transform[plane] * glm::vec4(p2d_plane, -1, 1); bb_min = glm::min(bb_min, p2d_plane_raster); bb_max = glm::max(bb_max, p2d_plane_raster); //glm::vec3 pt_o, pt_d; //Canvas::I->point_unproject(p2d, pt_o, pt_d); vertex_t v; v.pos = glm::vec4(xyz(p3d_world), 1); v.uvs = p2d_plane_raster; m_points_face[plane].push_back(v); } if (m_points_face[plane].size() < 3) { m_shape[plane].clear(); m_points_face[plane].clear(); continue; } auto bb_sz = bb_max - bb_min; for (auto& v : m_points_face[plane]) { v.uvs2 = v.uvs / Canvas::I->m_size; v.uvs = (v.uvs - bb_min) / bb_sz; v.pos = center_mat * v.pos; } auto shape3d = triangulate(m_points_face[plane]); App::I->render_task([&] { m_shape[plane].update_vertices(shape3d.data(), (int)shape3d.size()); Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->m_rtt[plane].bindFramebuffer(); m_tex[plane].create(bb_sz.x, bb_sz.y); m_tex[plane].bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bb_min.x, bb_min.y, bb_sz.x, bb_sz.y); m_tex[plane].unbind(); Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->m_rtt[plane].unbindFramebuffer(); }); m_commit_on_leave = true; } if (m_action == ActionType::Cut) { auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx]; glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); auto action = new ActionStroke; action->was_saved = !Canvas::I->m_unsaved; for (int i = 0; i < 6; i++) { auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]); auto mvp = proj * plane_camera * m_xform * m_xform_local; glm::vec2 bb_min(Canvas::I->m_size); glm::vec2 bb_max(0, 0); for (int j = 0; j < 6; j++) { for (auto p : m_points_face[j]) { auto p_clip = mvp * p.pos; auto p_norm = p_clip / p_clip.w; if (p_clip.w < 0 || glm::any(glm::greaterThan(glm::abs(xy(p_norm)), { 1, 1 }))) continue; auto p_raster = (xy(p_norm) * 0.5f + 0.5f) * Canvas::I->m_size; bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster)); bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster)); } } glm::vec2 pad(2); bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad); bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad); auto bb_sz = bb_max - bb_min; if (bb_sz.x <= 0.f || bb_sz.y <= 0.f) continue; action->m_image[i] = std::make_unique(bb_sz.x * bb_sz.y * 4); action->m_box[i] = { bb_min, bb_max }; action->m_old_box[i] = layer->m_dirty_box[i]; action->m_old_dirty[i] = layer->m_dirty_face[i]; layer->m_dirty_face[i] = true; layer->m_dirty_box[i] = { glm::min(xy(layer->m_dirty_box[i]), bb_min), glm::max(zw(layer->m_dirty_box[i]), bb_max), }; App::I->render_task([&] { glViewport(0, 0, layer->w, layer->h); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE0); ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 0 }); layer->m_rtt[i].bindFramebuffer(); // copy framebuffer to action data glReadPixels(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); for (int j = 0; j < 6; j++) m_shape[j].draw_fill(); layer->m_rtt[i].unbindFramebuffer(); }); } action->m_layer_idx = Canvas::I->m_current_layer_idx; action->m_canvas = Canvas::I; //action->m_stroke = std::move(m_current_stroke); ActionManager::add(action); m_source_image.destroy(); } Canvas::I->pop_camera(); } void CanvasModeTransform::leave(kCanvasMode next) { if (!m_commit_on_leave) return; auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx]; glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); auto action = new ActionStroke; action->was_saved = !Canvas::I->m_unsaved; for (int i = 0; i < 6; i++) { auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]); auto mv = plane_camera * m_xform * m_xform_local; auto mvp = proj * mv; std::vector poly2d; static std::vector face_corners{ {1,1}, {-1,1}, {-1,-1}, {1,-1} }; for (int j = 0; j < 6; j++) { std::vector poly_cam; poly_cam.reserve(m_points_face[j].size()); for (auto p : m_points_face[j]) poly_cam.push_back(mv * p.pos); auto poly_clipped = poly_clip_near(poly_cam, 0.01f); for (auto p : poly_clipped) { auto p_clip = proj * glm::vec4(p, 1); if (p_clip.w < 0) continue; auto p_norm = p_clip / p_clip.w; poly2d.push_back(p_norm); } } auto clipped = poly_intersect(poly2d, face_corners); glm::vec2 bb_min(Canvas::I->m_size); glm::vec2 bb_max(0, 0); for (auto p_norm : clipped) { auto p_raster = (p_norm * 0.5f + 0.5f) * Canvas::I->m_size; bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster)); bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster)); } glm::vec2 pad(2); bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad); bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad); auto bb_sz = bb_max - bb_min; if (clipped.empty() || bb_sz.x <= 0.f || bb_sz.y <= 0.f) continue; action->m_image[i] = std::make_unique(bb_sz.x * bb_sz.y * 4); action->m_box[i] = { bb_min, bb_max }; action->m_old_box[i] = layer->m_dirty_box[i]; action->m_old_dirty[i] = layer->m_dirty_face[i]; layer->m_dirty_face[i] = true; layer->m_dirty_box[i] = { glm::min(xy(layer->m_dirty_box[i]), bb_min), glm::max(zw(layer->m_dirty_box[i]), bb_max), }; App::I->render_task([&] { layer->m_rtt[i].bindFramebuffer(); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE0); glViewport(0, 0, layer->m_rtt[i].getWidth(), layer->m_rtt[i].getHeight()); // save fb content for history glReadPixels(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); // copy fb content to texture for blending glActiveTexture(GL_TEXTURE0); Canvas::I->m_tex2[i].bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, bb_min.x, bb_min.y, bb_min.x, bb_min.y, bb_sz.x, bb_sz.y); // slot for m_tex glActiveTexture(GL_TEXTURE1); for (int j = 0; j < 6; j++) { ShaderManager::use(kShader::CompDraw); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); ShaderManager::u_float(kShaderUniform::Alpha, 1); ShaderManager::u_int(kShaderUniform::UseFragcoord, true); ShaderManager::u_int(kShaderUniform::Lock, false); ShaderManager::u_int(kShaderUniform::Mask, false); ShaderManager::u_vec2(kShaderUniform::Resolution, Canvas::I->m_size); ShaderManager::u_int(kShaderUniform::BlendMode, 0); ShaderManager::u_int(kShaderUniform::UseDual, false); ShaderManager::u_int(kShaderUniform::UsePattern, false); Canvas::I->m_sampler_nearest.bind(1); Canvas::I->m_sampler_nearest.bind(0); m_tex[j].bind(); m_shape[j].draw_fill(); m_tex[j].unbind(); } layer->m_rtt[i].unbindFramebuffer(); }); } action->m_layer_idx = Canvas::I->m_current_layer_idx; action->m_canvas = Canvas::I; //action->m_stroke = std::move(m_current_stroke); ActionManager::add(action); layer->optimize(); //auto m = static_cast(Canvas::I->modes[(int)kCanvasMode::MaskFree][0]); //m->clear(); } void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { bool depth = glIsEnabled(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); for (int i = 0; i < 6; i++) { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * m_xform * m_xform_local); ShaderManager::u_vec4(kShaderUniform::Col, { 0, 1, 1, .1 }); m_shape[i].draw_fill(); ShaderManager::use(kShader::Texture); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * m_xform * m_xform_local); glActiveTexture(GL_TEXTURE0); m_tex[i].bind(); Canvas::I->m_sampler.bind(0); m_shape[i].draw_fill(); m_tex[i].unbind(); } ShaderManager::use(kShader::Color); auto m2d = Canvas::I->m_proj * Canvas::I->m_mv * m_xform * m_xform_local; for (int i = 0; i < corners.size(); i++) { auto c = m2d * glm::vec4(corners[i], 1); if (c.w < 0) continue; auto c3d = c / c.w; auto c2d = (xy(c3d) * 0.5f + 0.5f) * zw(Canvas::I->m_box); ShaderManager::u_mat4(kShaderUniform::MVP, ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom)); // draw inside ShaderManager::u_vec4(kShaderUniform::Col, { 1, 1, 1, i == corner_hl ? 1.f : .1f }); m_circle.draw_fill(); // draw black border ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); m_circle.draw_stroke(); } if (depth) glEnable(GL_DEPTH_TEST); } void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { auto m2d = glm::scale(glm::vec3(1, -1, 1)) * Canvas::I->m_proj * Canvas::I->m_mv * m_xform * m_xform_local; switch (me->m_type) { case kEventType::MouseDownR: { } break; case kEventType::MouseUpL: m_dragging = false; corner_hl = -1; break; case kEventType::MouseDownL: corner_hl = -1; corners2d.resize(corners.size()); for (int i = 0; i < corners.size(); i++) { auto c = m2d * glm::vec4(corners[i], 1); corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box); float d = glm::distance(corners2d[i], loc); if (d < 20.f * App::I->zoom) corner_hl = i; } if (corner_hl != -1) { m_dragging = true; m_drag_start = loc; m_drag_xform = m_xform; m_drag_xform_local = m_xform_local; m_drag_corner = corner_hl; m_drag_corners2d = corners2d; if (m_drag_corner < 4) { m_drag_diag = glm::distance(corners2d[4], corners2d[m_drag_corner]); } } break; case kEventType::MouseMove: { corner_hl = -1; corners2d.resize(corners.size()); for (int i = 0; i < corners.size(); i++) { auto c = m2d * glm::vec4(corners[i], 1); corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box); float d = glm::distance(corners2d[i], loc); if (d < 20.f * App::I->zoom) corner_hl = i; } if (m_dragging) { auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1); //auto diff = glm::radians(loc - m_drag_start) * 0.1f; //auto m = glm::eulerAngleXY(-diff.y, -diff.x); //m_xform = m * m_drag_xform; if (m_drag_corner > -1 && m_drag_corner < 4) { auto diag = glm::distance(corners2d[4], loc); auto scale = diag / m_drag_diag; m_xform_local = m_drag_xform_local * glm::scale(glm::vec3(scale, scale, 1)); } if (m_drag_corner == 4) { m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(loc), xyz(cam_up))); } if (m_drag_corner == 5) { auto a = glm::normalize(m_drag_corners2d[m_drag_corner] - m_drag_corners2d[4]); auto b = glm::normalize(loc - m_drag_corners2d[4]); auto angle = glm::orientedAngle(a, b); m_xform_local = m_drag_xform_local * glm::eulerAngleZ(-angle); } } /* { auto p2d = loc; //p2d.y = Canvas::I->m_box.w - p2d.y - 1; auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f) * glm::vec2(1, -1); auto p3d_plane = Canvas::I->m_plane_unproject[0] * glm::vec4(p2d_clip, 0, 1); auto p2d_plane = -p3d_plane / p3d_plane.z; // auto p3d_world = Canvas::I->m_plane_transform[0] * glm::vec4(p2d_plane, -1, 1); int x = 0; LOG("pt %f %f %f %f", p2d_plane.x, p2d_plane.y, p2d_plane.z, p2d_plane.w); } */ } break; default: break; } } //////////////////////////////////////////////////////////////////// void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { #if defined(__IOS__) m_draw_tip = (me->m_source == kEventSource::Mouse && me->m_type != kEventType::MouseUpL); #else m_draw_tip = (me->m_source == kEventSource::Mouse || me->m_source == kEventSource::Stylus); #endif if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) return; struct ActionFloodFill : public Action { std::shared_ptr m_layer; std::shared_ptr m_snap; std::array m_dirty_face = SIXPLETTE(false); std::array m_dirty_box = SIXPLETTE(glm::vec4(0)); glm::ivec2 m_pos; glm::vec4 m_color; float m_threshold; int m_layer_index; int m_plane; virtual void run() override { } virtual size_t memory() override { return m_snap->memsize(); } virtual Action* get_redo() override { auto a = new ActionFloodFill; a->m_direction = reverse_direction(); a->m_layer = m_layer; a->m_snap = m_snap; a->m_pos = m_pos; a->m_color = m_color; a->m_layer_index = m_layer_index; a->m_threshold = m_threshold; a->m_plane = m_plane; a->m_dirty_box = m_dirty_box; a->m_dirty_face = m_dirty_face; return a; } virtual void undo() override { if (m_direction == Direction::Undo) { m_layer->restore(*m_snap); } else { Canvas::FloodData plane_data; std::unique_ptr color; Canvas::I->flood_fill(m_layer_index, m_plane, { m_pos }, plane_data, m_threshold, m_color, color); plane_data.apply(); } } }; switch (me->m_type) { case kEventType::MouseUpL: glm::vec3 ro, rd, hit, n; glm::vec2 pos; int plane; if (Canvas::I->point_trace(loc, ro, rd, hit, pos, n, plane)) { Canvas::FloodData plane_data; std::unique_ptr color; Canvas::I->flood_fill(Canvas::I->m_current_layer_idx, plane, { (glm::ivec2)pos }, plane_data, m_tool->get_threshold(), Canvas::I->m_current_brush->m_tip_color, color); auto a = new ActionFloodFill; a->m_direction = Action::Direction::Undo; a->m_layer = plane_data.layer; a->m_snap = std::make_shared(plane_data.layer->snapshot()); a->m_pos = (glm::ivec2)pos; a->m_color = Canvas::I->m_current_brush->m_tip_color; a->m_layer_index = Canvas::I->m_current_layer_idx; a->m_threshold = m_tool->get_threshold(); a->m_plane = plane; a->m_dirty_box = plane_data.layer->m_dirty_box; a->m_dirty_face = plane_data.layer->m_dirty_face; ActionManager::add(a); plane_data.apply(); } break; default: break; } } void CanvasModeFloodFill::init() { TextureManager::load(m_cursor_path.c_str()); } void CanvasModeFloodFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { if (m_draw_tip) { ShaderManager::use(kShader::Texture); ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(1, -1, 1)) * ortho * glm::translate(glm::vec3(Canvas::I->m_cur_pos, 0.f)) * glm::scale(glm::vec3(32, 32, 1)) * glm::translate(glm::vec3(0.5f, -0.5f, 0)) ); ShaderManager::u_int(kShaderUniform::Tex, 0); auto& t = TextureManager::get(m_cursor_id); Canvas::I->m_sampler_linear.bind(0); t.bind(); Canvas::I->m_plane.draw_fill(); t.unbind(); } } void CanvasModeFloodFill::enter(kCanvasMode prev) { auto tools = App::I->layout[App::I->main_id]->find("tools-container"); m_tool = tools->add_child(); } void CanvasModeFloodFill::leave(kCanvasMode next) { m_tool->destroy(); m_tool = nullptr; }