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; }

相关新闻

多语言句子对推理驱动事实核查与内容审核

多语言句子对推理驱动事实核查与内容审核

自然语言推理是自然语言处理领域的一项核心任务,旨在判断两个句子之间的逻辑关系。Kaggle入门竞赛“Contradictory, My Dear Watson”提供了一个典型的多语言自然语言推理场景,要求参赛者对包含前提和假设的句子对进行分类,判断其关系属于“蕴含”、“中立”还是“矛盾”。该…

2026/11/5 10:32:47 阅读更多 →
埃姆斯住宅房价预测与自动化估值建模

埃姆斯住宅房价预测与自动化估值建模

在数据科学的学习路径上,找到一个兼具经典性、完整性与适度挑战性的入门项目至关重要。Kaggle上的“房价预测竞赛”正是这样一个标杆。它要求参赛者利用美国爱荷华州埃姆斯市的住宅数据,构建模型预测房屋最终售价。该竞赛脱胎于经典的波士顿房价数据集,但提供了更丰富、更现…

2026/11/3 18:37:06 阅读更多 →
飞船乘客状态预测与金融风控建模启发

飞船乘客状态预测与金融风控建模启发

在数据科学的学习路径上,理论知识需要通过具体的项目实践来巩固和深化。Kaggle 平台上的 “Spaceship Titanic” 竞赛,以其清晰的二分类任务、适中的数据规模以及面向初学者的定位,成为掌握表格数据建模全流程的理想起点。该竞赛要求基于乘客的个人记录,预测其是否在一次太…

2026/11/7 6:16:24 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/14 5:13:29 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/14 5:13:29 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/14 5:13:29 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/13 22:08:41 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/14 2:41:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/14 5:13:29 阅读更多 →

日新闻

周新闻

月新闻