#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" NodeCanvas* CanvasMode::node; ui::Canvas* CanvasMode::canvas; void CanvasModeBasicCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { switch (me->m_type) { case kEventType::MouseDownL: break; case kEventType::MouseUpL: break; case kEventType::MouseDownR: m_draggingR = true; m_dragR_start = me->m_pos; m_pan_start = canvas->m_pan; node->mouse_capture(); break; case kEventType::MouseUpR: m_draggingR = false; node->mouse_release(); break; case kEventType::MouseMove: if (m_draggingR) canvas->m_pan = m_pan_start + (me->m_pos - m_dragR_start) * glm::vec2(-1, -1) * (canvas->m_cam_fov / 85.f); canvas->m_cam_rot = canvas->m_pan * 0.003f; break; case kEventType::MouseScroll: m_zoom_canvas += me->m_scroll_delta * 0.1f; canvas->m_cam_fov -= me->m_scroll_delta * 20.1f; 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->m_pan; m_zoom_start = m_zoom_canvas; m_camera_fov = canvas->m_cam_fov; break; case kEventType::GestureMove: canvas->m_pan = m_pan_start + ge->m_pos_delta * glm::vec2(-1, -1) * 0.3f * (canvas->m_cam_fov / 85.f); canvas->m_cam_fov = m_camera_fov - ge->m_distance_delta * .05f; canvas->m_cam_rot = canvas->m_pan * 0.003f; App::I.brush_update(); break; default: break; } } //////////////////////////////////////////////////////////////////// void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { switch (me->m_type) { case kEventType::MouseDownL: if (App::I.keys[(int)kKey::KeyAlt] || m_picking) { m_picking = true; canvas->pick_start(); //canvas->m_show_tmp = true; } else { canvas->stroke_start(loc, me->m_pressure, node->m_brush); } m_dragging = true; node->mouse_capture(); break; case kEventType::MouseUpL: if (m_dragging && !m_picking) { node->mouse_release(); canvas->stroke_end(); } if (m_dragging && m_picking) { node->mouse_release(); //canvas->m_show_tmp = false; glm::vec4 pix = canvas->pick_get(loc); auto hsv = convert_rgb2hsv(glm::vec3(pix[0], pix[1], pix[2])); App::I.color->m_hue->set_value(hsv.x); App::I.color->m_quad->set_value(1.f - hsv.y, 1.f - hsv.z); } m_dragging = false; m_picking = false; break; case kEventType::MouseMove: if (m_dragging && !m_picking) canvas->stroke_update(loc, me->m_pressure); if (m_dragging && m_picking) { glm::vec4 pix = canvas->pick_get(loc); auto hsv = convert_rgb2hsv(glm::vec3(pix[0], pix[1], pix[2])); App::I.color->m_hue->set_value(hsv.x); App::I.color->m_quad->set_value(1.f - hsv.y, 1.f - hsv.z); } m_cur_pos = loc; break; case kEventType::MouseCancel: if (m_dragging) { canvas->stroke_cancel(); m_dragging = false; node->mouse_release(); } if (m_picking) { m_picking = false; } break; default: break; } } void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { #ifndef __IOS__ if (!m_dragging && !m_picking) { ui::ShaderManager::use(ui::kShader::StrokePreview); ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0); ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, node->m_brush.m_tip_flow); auto tip_color = glm::vec4(glm::vec3(node->m_brush.m_tip_color), 1); ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, tip_color); ui::ShaderManager::u_int(ui::kShaderUniform::Highlight, 0); float tip_scale = 1.f / glm::tan(glm::radians(ui::Canvas::I->m_cam_fov * 0.5f)); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, glm::scale(glm::vec3(1, -1, 1)) * ortho * glm::translate(glm::vec3(m_cur_pos, 0)) * glm::scale(glm::vec3(node->m_brush.m_tip_size * 800.f * tip_scale)) ); glEnable(GL_BLEND); glActiveTexture(GL_TEXTURE0); auto& tex = TextureManager::get(node->m_brush.m_tex_id); tex.bind(); canvas->m_sampler_brush.bind(0); canvas->m_plane.draw_fill(); tex.unbind(); } #endif } void CanvasModePen::leave() { m_brush = node->m_brush; } void CanvasModePen::enter() { if (m_valid_brush) { node->m_brush = m_brush; App::I.brush_update(); } else { m_brush = node->m_brush; m_valid_brush = true; } } //////////////////////////////////////////////////////////////////// void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { 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) { canvas->stroke_start(m_drag_start, 1.f, node->m_brush); canvas->stroke_update(m_drag_pos, 1.f); canvas->stroke_end(); } m_dragging = false; break; case kEventType::MouseMove: if (m_dragging) m_drag_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) { ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho); ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, node->m_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->m_box.w - AB[0].y - 1; // invert Y AB[1].y = canvas->m_box.w - AB[1].y - 1; // invert Y m_line.update_vertices(AB); m_line.draw_stroke(); } } void CanvasModeLine::init() { m_line.create(); } //////////////////////////////////////////////////////////////////// void CanvasModeCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { switch (me->m_type) { case kEventType::MouseDownR: canvas->m_cam_pos = { 0, 0, 0 }; break; case kEventType::MouseDownL: m_dragging = true; m_drag_start = me->m_pos; m_pos_start = canvas->m_cam_pos.xy; node->mouse_capture(); break; case kEventType::MouseUpL: m_dragging = false; node->mouse_release(); canvas->m_cam_pos = { 0, 0, 0 }; break; case kEventType::MouseMove: if (m_dragging) canvas->m_cam_pos.xy = m_pos_start + (me->m_pos - m_drag_start) * glm::vec2(1, -1) * 0.001f; break; case kEventType::MouseCancel: m_dragging = false; node->mouse_release(); break; default: break; } } //////////////////////////////////////////////////////////////////// void CanvasModeGrid::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { switch (me->m_type) { case kEventType::MouseDownL: { node->mouse_capture(); glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 fb_pos; if (canvas->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, m_plane_id)) { m_lines.push_back({ hit_o, hit_d }); origin = hit_o; dir = hit_d; m_dragging = true; } break; } case kEventType::MouseUpL: node->mouse_release(); m_dragging = false; //commit(); break; case kEventType::MouseMove: { glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 hit_fb; if (m_dragging && canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, m_plane_id)) { m_lines.back() = { hit_o, hit_d }; origin = hit_o; dir = hit_d; m_dragging = true; } break; } case kEventType::MouseCancel: if (m_dragging) 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) { //if (m_dragging) for (auto l : m_lines) { auto origin = l.o; auto dir = l.d; ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera); ui::ShaderManager::u_vec4(ui::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(); } } void CanvasModeGrid::init() { m_line.create(); } void CanvasModeGrid::commit() { auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj){ ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera); ui::ShaderManager::u_vec4(ui::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->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2)); } void CanvasModeGrid::clear() { m_lines.clear(); } //////////////////////////////////////////////////////////////////// void CanvasModeMaskFree::init() { m_shape.create(); } void CanvasModeMaskFree::leave() { // canvas->draw_objects(std::bind(&CanvasModeFill::on_Draw, this, glm::mat4(), std::placeholders::_1, std::placeholders::_2)); // m_points.clear(); } int SegmentCount = 0; std::vector CanvasModeMaskFree::triangulate(const std::vector>& points) { struct Segment { std::shared_ptr a = nullptr; std::shared_ptr b = nullptr; Segment* prev = nullptr; std::shared_ptr next = nullptr; bool end = false; Segment() { SegmentCount++; } ~Segment() { SegmentCount--; } }; std::shared_ptr root = std::make_shared(); std::shared_ptr node = root; for (int i = 0; i < points.size(); i++) { node->a = points[i]; if (i == points.size() - 1) { node->b = points[0]; node->next = root; node->end = true; root->prev = node.get(); } else { node->b = points[i + 1]; node->next = std::make_shared(); node->next->prev = node.get(); } node = node->next; } node = root; std::stack> todo; std::vector> polys; todo.push(root); while (!todo.empty()) { node = todo.top(); todo.pop(); polys.push_back(node); while (node) { std::shared_ptr other = node->next; while (other) { if (*node->a == *other->a || *node->a == *other->b || *node->b == *other->a || *node->b == *other->b) { other = other->end ? nullptr : other->next; continue; } glm::vec2 s0a(node->a->x, node->a->y); glm::vec2 s0b(node->b->x, node->b->y); glm::vec2 s1a(other->a->x, other->a->y); glm::vec2 s1b(other->b->x, other->b->y); glm::vec2 is; if (segments_intersect(s0a, s0b, s1a, s1b, is)) { auto p = std::make_shared(is.x, is.y); auto poly_root = std::make_shared(); poly_root->a = p; poly_root->b = node->b; poly_root->next = node->next; todo.push(poly_root); other->a = p; node->b = p; auto poly_end = std::make_shared(); poly_end->a = other->prev->b; poly_end->b = p; poly_end->end = true; poly_end->prev = other->prev; other->prev->next = poly_end; other->prev = node.get(); node->next = other; break; } other = other->end ? nullptr : other->next; } node = node->end ? nullptr : node->next; } } std::vector vertices; for (auto poly : polys) { std::vector outline; node = poly; while (node) { outline.push_back(new p2t::Point(*node->a)); auto current = node; node = node->end ? nullptr : node->next; current->next = nullptr; } LOG("poly %ld", outline.size()); if (outline.size() > 2) { p2t::CDT* cdt = new p2t::CDT(outline); cdt->Triangulate(); auto tr = cdt->GetTriangles(); for (auto t : tr) { ui::Shape::vertex_t vertex; for (int i = 0; i < 3; i++) { auto p = t->GetPoint(i); glm::vec3 ro, rd, hit_o, hit_d; glm::vec2 hit_fb; int plane_id; if (canvas->point_trace({p->x, p->y}, ro, rd, hit_o, hit_fb, hit_d, plane_id)) { m_dirty_planes[plane_id]++; vertex.pos = glm::vec4(hit_o, 1); vertices.push_back(vertex); } } } delete cdt; } for (auto p : outline) delete p; } return vertices; } void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { static glm::vec2 oldpos; static glm::vec2 oldvec; static float acc = 0.f; 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; ui::Shape::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); canvas->m_smask.clear({0, 0, 0, 0}); canvas->m_smask_active = true; break; } case kEventType::MouseUpL: node->mouse_release(); m_dragging = false; if (m_points2d.size() > 3) { std::vector> points; for (int i = 0; i < (int)m_points2d.size() - 1; i++) points.emplace_back(std::make_shared(m_points2d[i].x, m_points2d[i].y)); auto v = triangulate(points); LOG("leaked segments: %d", SegmentCount); 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); ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera); ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 1, 1, 1}); m_shape.draw_fill(); }; canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), canvas->m_smask); m_points.clear(); } } else { canvas->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-glm::dot(v, oldvec); acc += d; oldpos = loc; oldvec = v; if (acc > 0.001) // angle change tollerance { LOG("d=%f acc=%f", d, acc); acc = 0; m_points2d.push_back(loc); ui::Shape::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) { if (!m_points.empty()) { ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, glm::scale(glm::vec3(1,-1,1)) * ortho); ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 }); m_dragging ? m_shape.draw_stroke() : m_shape.draw_fill(); } } //////////////////////////////////////////////////////////////////// void CanvasModeFill::init() { m_shape.create(); } void CanvasModeFill::leave() { if (m_points.size() > 2) { auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) { ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera); ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 1, 1, 1}); m_shape.draw_fill(); }; canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), canvas->m_smask); m_points.clear(); canvas->m_smask_active = true; } else { canvas->m_smask_active = false; } } void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { 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->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0)) { m_dirty_planes[plane_id]++; ui::Shape::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->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->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, plane_id)) { ui::Shape::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()) { ui::ShaderManager::use(ui::kShader::Color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera); ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, .25 }); m_shape.draw_fill(); } }