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/8/26 16:52:57 阅读更多 →
埃姆斯住宅房价预测与自动化估值建模

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

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

2026/8/26 16:52:57 阅读更多 →
飞船乘客状态预测与金融风控建模启发

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

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

2026/8/26 16:52:57 阅读更多 →

最新新闻

八分之一三阶滤波系数优化技巧

八分之一三阶滤波系数优化技巧

摘要:在资源受限的STM32F0等无硬件乘法器MCU上,系数为[1,2,2,3]的三阶FIR滤波器通过整数系数简化乘法、2的幂次分母实现快速移位归一化,以极低计算开销获得确定性滤波效果。本文解析该方案如何在计算效率、资源消耗与滤波性能间取得最佳平衡,成为嵌入式实时信号处理的优化典…

2026/8/26 18:44:22 阅读更多 →
C#中的lock锁

C#中的lock锁

lock 锁详解一、先解释:lock 是什么? lock 是 C# 中用于"多线程同步"的关键字,它保证同一时刻只有一个线程能进入被锁住的代码块。 lock (锁对象) {// 临界区:同一时刻只允许一个线程执行这里 }核心概念:概念…

2026/8/26 18:44:22 阅读更多 →
C++学习笔记(聊天服务器)

C++学习笔记(聊天服务器)

虚函数 虚函数 给基类留一个 “接口空位”,让子类可以替换掉这个函数的实现;调用的时候,自动跑子类的代码,而不是基类的。这个行为叫多态。 如果不加virtual:调用哪个函数,看指针 / 引用的类型&#xff0c…

2026/8/26 18:44:22 阅读更多 →
控制 SELinux 文件上下文

控制 SELinux 文件上下文

初始 SELinux 上下文所有资源(如进程、文件和端口)都标有 SELinux 上下文。SELinux 在 /etc/selinux/targeted/contexts/files/ 目录中维护基于文件的文件标签策略数据库。新文件在文件名与现有标签策略匹配时获得默认标签。当新文件的名称与现有标签策略…

2026/8/26 18:44:22 阅读更多 →
MoonBit v0.10.9 版本更新

MoonBit v0.10.9 版本更新

MoonBit v0.10.4版本更新 对应moonc版本: v0.10.9 语言更新with-pattern 现在需要在使用了 with 的分支显示加上括号,便于读者更容易理解 with-pattern 的优先级。可以使用 moon fmt 进行自动迁移: fn main {let a Some("hello")match a {Som…

2026/8/26 18:44:21 阅读更多 →
绍兴近杭但不贵:临杭产业园的土地成本优势观察本

绍兴近杭但不贵:临杭产业园的土地成本优势观察本

位于绍兴诸暨市次坞镇的临杭产业园,近年来因其独特的区位条件与土地成本优势,逐渐进入杭州及周边地区制造型企业的选址视野。该园区坐落于诸暨北大门次坞镇,地处G60科创走廊与杭绍甬一体化发展轴的交汇点。距杭州主城区约38公里,经…

2026/8/26 18:43:21 阅读更多 →

日新闻

Python random 模块常用函数详解:从入门到实战

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 0:00:40 阅读更多 →
《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》索引目录: 《Microsoft Sql server 2008 Internals》读书笔记--目录索引 在上篇文章中,主要介绍了创建数据库的基本语法和FileGroup的初步知识。需要注意的是: 关于FileGroup 如果你的系统是用Raid设备直接存…

2026/8/26 1:18:18 阅读更多 →
政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体已经从概念试点阶段,转入了政务服务的常态化落地应用;在实际使用过程中,它能自主理解办事需求、辅助完成填报申报、开展材料预审,并联动多个系统协同作业,真正嵌入到政务办理的全流程当中。但在落地推进过…

2026/8/26 1:18:18 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/26 14:45:33 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/26 17:46:43 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 14:46:37 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/26 17:46:39 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →