ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

bambustudio 选择底面 修复

bambustudio 选择底面 修复 目录设置和选择设备自动修复结束bambustudio 选择底面1. 鼠标点击面 → 获取法向量2. m_normal 的填充3. 实际旋转执行 ← 核心调用链总结检测平面设置和选择设备m_device_manager-set_selected_machine(obj-get_dev_id()); MachineObject* obj_ dev-get_selected_machine();自动修复结束void Plater::priv::on_repair_model(wxCommandEvent event) { wxGetApp().obj_list()-fix_through_netfabb(); }bambustudio 选择底面选择底面Lay on face功能的完整调用链1. 鼠标点击面 → 获取法向量GLGizmosManager::on_mouseGLGizmosManager.cpp:983-997用户点击某个面grabber时检测到m_current Flatten调用m_parent.do_flatten(get_flattening_normal(), L(Tool-Lay on Face));其中get_flattening_normal()GLGizmosManager.cpp:548转发到GLGizmoFlatten::get_flattening_normal()GLGizmoFlatten.cpp:571返回并清空成员变量m_normal。2.m_normal的填充GLGizmoFlatten::on_start_draggingGLGizmoFlatten.cpp:111-123cppif (m_hover_id ! -1) { if (m_faltten_type FlattenType::Default) m_normal m_planes[m_hover_id].normal; // 从预计算的平面取法向量 else m_normal m_hit_object_normal.castdouble(); // Triangle 模式取射线命中面法向量 }FlattenType::Default点击预计算好的平面图标大平面m_planes由update_planes()GLGizmoFlatten.cpp:296生成FlattenType::Triangle点击任意三角形面法向量来自update_raycast_cacheGLGizmoFlatten.cpp:125中的射线检测3. 实际旋转执行 ←核心GLCanvas3D::do_flattenGLCanvas3D.cpp:6420-6427cppvoid GLCanvas3D::do_flatten(const Vec3d normal, const std::string snapshot_type) { if (!snapshot_type.empty()) wxGetApp().plater()-take_snapshot(snapshot_type); m_selection.flattening_rotate(normal); // ← 旋转 do_rotate(); // 触发重绘/同步 }Selection::flattening_rotateSelection.cpp:1491-1526是真正执行旋转的函数cpp// 将选中面的法向量旋转到朝下-Z方向 const Transform3d rotation_matrix Transform3d(Eigen::Quaterniond().setFromTwoVectors(tnormal, -Vec3d::UnitZ())); v.set_instance_transformation( old_inst_trafo.get_offset_matrix() * rotation_matrix * old_inst_trafo.get_matrix_no_offset());核心逻辑用Eigen::Quaterniond::setFromTwoVectors(tnormal, -Vec3d::UnitZ())计算一个旋转把选中面的法向量tnormal对齐到世界坐标的-Z朝下然后写回 instance 变换矩阵。调用链总结鼠标点击面 → GLGizmosManager::on_mouse (GLGizmosManager.cpp:994) → get_flattening_normal() (GLGizmoFlatten.cpp:571) └ m_normal 在 on_start_dragging() 中填充 (GLGizmoFlatten.cpp:111) → GLCanvas3D::do_flatten() (GLCanvas3D.cpp:6420) → Selection::flattening_rotate() (Selection.cpp:1491) ← 实际旋转如果你想程序化触发选择底面需要设置好m_normal后调用do_flatten。m_normal是 private 成员正常路径只能通过点击触发on_start_dragging填充——程序化调用需手动构造法向量并直接调do_flatten。检测平面void GLGizmoFlatten::update_planes() { const ModelObject* mo m_c-selection_info()-model_object(); TriangleMesh ch; for (const ModelVolume* vol : mo-volumes) { if (vol-type() ! ModelVolumeType::MODEL_PART) continue; TriangleMesh vol_ch vol-get_convex_hull(); vol_ch.transform(vol-get_matrix()); ch.merge(vol_ch); } ch ch.convex_hull_3d(); m_planes.clear(); const Transform3d inst_matrix mo-instances.front()-get_matrix(true); // Following constants are used for discarding too small polygons. const float experted_minimal_area 5.0f; const float minimal_area 1.0f; // in square mm (world coordinates) const float minimal_side 1.f; // mm const float minimal_angle 1.f; // degree, initial value was 10, but cause bugs // Now well go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. const int num_of_facets ch.facets_count(); const std::vectorVec3f face_normals its_face_normals(ch.its); const std::vectorVec3i face_neighbors its_face_neighbors(ch.its); std::vectorint facet_queue(num_of_facets, 0); std::vectorbool facet_visited(num_of_facets, false); int facet_queue_cnt 0; const stl_normal* normal_ptr nullptr; int facet_idx 0; while (1) { // Find next unvisited triangle: for (; facet_idx num_of_facets; facet_idx) if (!facet_visited[facet_idx]) { facet_queue[facet_queue_cnt ] facet_idx; facet_visited[facet_idx] true; normal_ptr face_normals[facet_idx]; m_planes.emplace_back(); break; } if (facet_idx num_of_facets) break; // Everything was visited already while (facet_queue_cnt 0) { int facet_idx facet_queue[-- facet_queue_cnt]; const stl_normal this_normal face_normals[facet_idx]; if (std::abs(this_normal(0) - (*normal_ptr)(0)) 0.001 std::abs(this_normal(1) - (*normal_ptr)(1)) 0.001 std::abs(this_normal(2) - (*normal_ptr)(2)) 0.001) { const Vec3i face ch.its.indices[facet_idx]; for (int j0; j3; j) m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].castdouble()); facet_visited[facet_idx] true; for (int j 0; j 3; j) if (int neighbor_idx face_neighbors[facet_idx][j]; neighbor_idx 0 ! facet_visited[neighbor_idx]) facet_queue[facet_queue_cnt ] neighbor_idx; } } m_planes.back().normal normal_ptr-castdouble(); Pointf3s verts m_planes.back().vertices; // Now well transform all the points into world coordinates, so that the areas, angles and distances // make real sense. verts transform(verts, inst_matrix); // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway): if (verts.size() 3 ((verts[0] - verts[1]).norm() minimal_side || (verts[0] - verts[2]).norm() minimal_side || (verts[1] - verts[2]).norm() minimal_side)) m_planes.pop_back(); } // Lets prepare transformation of the normal vector from mesh to instance coordinates. Geometry::Transformation t(inst_matrix); Vec3d scaling t.get_scaling_factor(); t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); // Now well go through all the polygons, transform the points into xy plane to process them: for (int polygon_id0; polygon_id m_planes.size(); polygon_id) { Pointf3s polygon m_planes[polygon_id].vertices; const Vec3d normal m_planes[polygon_id].normal; // transform the normal according to the instance matrix: Vec3d normal_transformed t.get_matrix() * normal; // We are going to rotate about z and y to flatten the plane Eigen::Quaterniond q; Transform3d m Transform3d::Identity(); m.matrix().block(0, 0, 3, 3) q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); polygon transform(polygon, m); // Now to remove the inner points. Well misuse Geometry::convex_hull for that, but since // it works in fixed point representation, we will rescale the polygon to avoid overflows. // And yes, it is a nasty thing to do. Whoever has time is free to refactor. Vec3d bb_size BoundingBoxf3(polygon).size(); float sf std::min(1./bb_size(0), 1./bb_size(1)); Transform3d tr Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); polygon transform(polygon, tr); polygon Slic3r::Geometry::convex_hull(polygon); polygon transform(polygon, tr.inverse()); // Calculate area of the polygons and discard ones that are too small float area m_planes[polygon_id].area; area 0.f; for (unsigned int i 0; i polygon.size(); i) // Shoelace formula area polygon[i](0)*polygon[i 1 polygon.size() ? i 1 : 0](1) - polygon[i 1 polygon.size() ? i 1 : 0](0)*polygon[i](1); area 0.5f * std::abs(area); bool discard false; if (area minimal_area) discard true; else { // We also check the inner angles and discard polygons with angles smaller than the following threshold const double angle_threshold ::cos(minimal_angle * (double)PI / 180.0); for (unsigned int i 0; i polygon.size(); i) { const Vec3d prec polygon[(i 0) ? polygon.size() - 1 : i - 1]; const Vec3d curr polygon[i]; const Vec3d next polygon[(i polygon.size() - 1) ? 0 : i 1]; if ((prec - curr).normalized().dot((next - curr).normalized()) angle_threshold) { discard true; break; } } } if (discard) { m_planes[polygon_id] std::move(m_planes.back()); m_planes.pop_back(); polygon_id--; continue; } // We will shrink the polygon a little bit so it does not touch the object edges: Vec3d centroid std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); centroid / (double)polygon.size(); for (auto vertex : polygon) vertex 0.9f*vertex 0.1f*centroid; // Polygon is now simple and convex, well round the corners to make them look nicer. // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex // towards their average (controlled by aggressivity). This is repeated k times. // In next iterations, the neighbours are not always taken at the middle (to increase the // rounding effect at the corners, where we need it most). const unsigned int k 10; // number of iterations const float aggressivity 0.2f; // agressivity const unsigned int N polygon.size(); std::vectorstd::pairunsigned int, unsigned int neighbours; if (k ! 0) { Pointf3s points_out(2*k*N); // vector long enough to store the future vertices for (unsigned int j0; jN; j) { points_out[j*2*k] polygon[j]; neighbours.push_back(std::make_pair((int)(j*2*k-k) 0 ? (N-1)*2*kk : j*2*k-k, j*2*kk)); } for (unsigned int i0; ik; i) { // Calculate middle of each edge so that neighbours points to something useful: for (unsigned int j0; jN; j) if (i0) points_out[j*2*kk] 0.5f * (points_out[j*2*k] points_out[jN-1 ? 0 : (j1)*2*k]); else { float r 0.20.3/(k-1)*i; // the neighbours are not always taken in the middle points_out[neighbours[j].first] r*points_out[j*2*k] (1-r) * points_out[neighbours[j].first-1]; points_out[neighbours[j].second] r*points_out[j*2*k] (1-r) * points_out[neighbours[j].second1]; } // Now we have a triangle and valid neighbours, we can do an iteration: for (unsigned int j0; jN; j) points_out[2*k*j] (1-aggressivity) * points_out[2*k*j] aggressivity*0.5f*(points_out[neighbours[j].first] points_out[neighbours[j].second]); for (auto n : neighbours) { n.first; --n.second; } } polygon points_out; // replace the coarse polygon with the smooth one that we just created } // Raise a bit above the object surface to avoid flickering: for (auto b : polygon) b(2) 0.1f; // Transform back to 3D (and also back to mesh coordinates) polygon transform(polygon, inst_matrix.inverse() * m.inverse()); } if (m_planes.size() 0) { m_show_warning true; return; } // Well sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData a, const PlaneData b) { return a.area b.area; }); auto delte_index_to_end [](int index, std::vectorPlaneData planes) { for (size_t i planes.size() - 1; i index; i--) { planes.pop_back(); } }; const int plane_count 30; for (size_t i 0; i m_planes.size(); i) { if (m_planes[i].area experted_minimal_area) { if (i 1 plane_count) { delte_index_to_end(plane_count, m_planes); break; } else {//plane_count for (size_t j i 1; j m_planes.size(); j) { if (j 1 plane_count) { delte_index_to_end(plane_count, m_planes); break; } } break; } } } m_planes.resize(std::min((int)m_planes.size(), 254)); // Planes are finished - lets save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); for (const ModelVolume* vol : mo-volumes) { m_volumes_matrices.push_back(vol-get_matrix()); m_volumes_types.push_back(vol-type()); } m_first_instance_scale mo-instances.front()-get_scaling_factor(); m_first_instance_mirror mo-instances.front()-get_mirror(); m_old_model_object mo; m_old_instance_id m_c-selection_info()-get_active_instance(); // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto plane : m_planes) { plane.vbo.reserve(plane.vertices.size()); for (const auto vert : plane.vertices) plane.vbo.push_geometry(vert, plane.normal); for (size_t i1; iplane.vertices.size()-1; i) plane.vbo.push_triangle(0, i, i1); // triangle fan plane.vbo.finalize_geometry(true); // FIXME: vertices should really be local, they need not // persist now when we use VBOs plane.vertices.clear(); plane.vertices.shrink_to_fit(); } m_show_warning false; m_planes_valid true; }
返回列表