#include "nmLayerGeometry.h" #include "nmDataLayer.h" #include #include nmLayerGeometryRow::nmLayerGeometryRow() : m_dTopDepth(0.0), m_dBottomDepth(0.0), m_dThickness(0.0), m_bLegacyGap(false) { } bool nmLayerGeometry::fromLayers(const QVector& layers, nmLayerGeometry& result) { // 先收齐所有层再赋给输出,避免某一层为空时留下只复制了一部分的结果。 // 此处只负责快照,不自动修复旧数据;打开窗口不应改变工程中原有的边界。 nmLayerGeometry copy; // 待输出的独立快照,全部层读取成功后才替换 result。 for (int i = 0; i < layers.size(); ++i) { const nmDataLayer* layer = layers.at(i); // 借用的持久化层对象,本函数不修改或释放它。 if (!layer) return false; nmLayerGeometryRow row; // 当前层的米制值记录,名称默认值仅补在快照中。 row.m_sName = layer->getName(); // 旧工程缺少名称时先在快照中补齐,插入/删除行后名称不再随行号漂移。 // 当前数据和原始快照采用同样规则比较,因此仅打开再确定不会触发写入。 if (row.m_sName.isEmpty()) row.m_sName = QString("Layer%1").arg(i + 1); row.m_clrLayer = layer->getColor(); row.m_dTopDepth = layer->getTop(); row.m_dBottomDepth = layer->getBottom(); row.m_dThickness = layer->getThickness(); row.m_bLegacyGap = layer->getIsChecked(); copy.m_vecLayers.append(row); } result = copy; return true; } bool nmLayerGeometry::isValid() const { if (m_vecLayers.isEmpty()) return false; double sum = 0.0; // 米制厚度累计值,仅用于检测总厚度是否溢出,不包含间隙。 for (int i = 0; i < m_vecLayers.size(); ++i) { const nmLayerGeometryRow& row = m_vecLayers.at(i); if (!qIsFinite(row.m_dTopDepth) || !qIsFinite(row.m_dBottomDepth) || !qIsFinite(row.m_dThickness) || row.m_dThickness <= 0.0 || row.m_dBottomDepth <= row.m_dTopDepth) return false; // 只容忍浮点加减的舍入误差,不能把不一致的厚度和边界当作有效几何提交。 // 按深度量级估计的浮点运算误差,单位 m;不代表业务上允许的层厚偏差。 const double tolerance = 8.0 * DBL_EPSILON * qMax(1.0, qMax(qAbs(row.m_dTopDepth), qAbs(row.m_dBottomDepth))); if (qAbs((row.m_dBottomDepth - row.m_dTopDepth) - row.m_dThickness) > tolerance) return false; if (i > 0 && row.m_dTopDepth < m_vecLayers.at(i - 1).m_dBottomDepth) return false; sum += row.m_dThickness; if (!qIsFinite(sum)) return false; } // 厚度总和可能有限,但连同较大历史间隙后的整体跨度仍可能溢出。 // 预览按整体跨度计算比例,因此也必须单独检查这个差值。 return qIsFinite(m_vecLayers.last().m_dBottomDepth - m_vecLayers.first().m_dTopDepth); } double nmLayerGeometry::totalThickness() const { // 不能用末层底深减首层顶深代替求和,否则旧工程的间隙会被计入几何总厚度。 double total = 0.0; // 各层自身厚度之和,单位 m,与整体顶底深度跨度不同。 for (int i = 0; i < m_vecLayers.size(); ++i) total += m_vecLayers.at(i).m_dThickness; return total; } bool nmLayerGeometry::equals(const nmLayerGeometry& other, bool geometryOnly) const { // 精确比较用于判断是否真的发生修改;容差只属于几何合法性检查,不能掩盖外部编辑。 if (m_vecLayers.size() != other.m_vecLayers.size()) return false; for (int i = 0; i < m_vecLayers.size(); ++i) { const nmLayerGeometryRow& a = m_vecLayers.at(i); // 当前快照中同一物理位置的层。 const nmLayerGeometryRow& b = other.m_vecLayers.at(i); // 待比较快照中的对应层,不转移所有权。 if (a.m_dTopDepth != b.m_dTopDepth || a.m_dBottomDepth != b.m_dBottomDepth || a.m_dThickness != b.m_dThickness || a.m_bLegacyGap != b.m_bLegacyGap) return false; if (!geometryOnly && (a.m_sName != b.m_sName || a.m_clrLayer != b.m_clrLayer)) return false; } return true; } QVector nmLayerGeometry::gaps() const { // 每项对应该层“上方”的实际距离;m_bLegacyGap 则描述该层“下方”是否分离。 // 两者方向不同,重排后由 relayout() 统一恢复旧格式标志。 QVector result(m_vecLayers.size(), 0.0); // 各层上方间距,单位 m;首项无上一层,固定为零。 for (int i = 1; i < m_vecLayers.size(); ++i) result[i] = m_vecLayers.at(i).m_dTopDepth - m_vecLayers.at(i - 1).m_dBottomDepth; return result; } bool nmLayerGeometry::relayout(double firstTop, const QVector& offsets) { // 按层顺序递推,最后统一验证;公开修改方法始终在临时副本上调用此函数。 // 即使中途遇到负间距、无效厚度或加法溢出,调用方的原几何也不会被部分改写。 if (m_vecLayers.isEmpty() || offsets.size() != m_vecLayers.size() || !qIsFinite(firstTop)) return false; double bottom = firstTop; // 递推中上一层的米制底深,首轮以指定顶深作为起点。 for (int i = 0; i < m_vecLayers.size(); ++i) { if (!qIsFinite(offsets.at(i)) || offsets.at(i) < 0.0) return false; m_vecLayers[i].m_dTopDepth = i == 0 ? firstTop : bottom + offsets.at(i); m_vecLayers[i].m_dBottomDepth = m_vecLayers.at(i).m_dTopDepth + m_vecLayers.at(i).m_dThickness; // 发生几何编辑后同步旧格式标志,使旧版读取程序仍能识别实际间隙。 m_vecLayers[i].m_bLegacyGap = i + 1 < offsets.size() && offsets.at(i + 1) > 0.0; bottom = m_vecLayers.at(i).m_dBottomDepth; } return isValid(); } bool nmLayerGeometry::setFirstTop(double top) { if (m_vecLayers.isEmpty()) return false; // 相同输入不重算,避免无操作时反复加减产生新的浮点舍入误差。 if (top == m_vecLayers.first().m_dTopDepth) return isValid(); nmLayerGeometry copy(*this); // 修改顶深的试算副本,重排成功后再整体替换自身。 if (!copy.relayout(top, gaps())) return false; *this = copy; return true; } bool nmLayerGeometry::setThickness(int row, double thickness) { if (row < 0 || row >= m_vecLayers.size() || !qIsFinite(thickness) || thickness <= 0.0) return false; // 原值未变且已有几何合法时直接完成;旧边界不一致时仍允许通过重排尝试修复。 if (thickness == m_vecLayers.at(row).m_dThickness && isValid()) return true; nmLayerGeometry copy(*this); // 修改厚度的试算副本,避免后续边界溢出时留下部分修改。 copy.m_vecLayers[row].m_dThickness = thickness; if (!copy.relayout(m_vecLayers.first().m_dTopDepth, gaps())) return false; *this = copy; return true; } bool nmLayerGeometry::insertLayer(int row, const QString& name, double thickness, const QColor& color) { if (row < 0 || row > m_vecLayers.size() || !isValid()) return false; nmLayerGeometry copy(*this); // 插入操作的候选几何,合法性检查通过前不影响原层列表。 QVector offsets = gaps(); // 按层索引排列的米制上方间距,随新层插入同步移位。 // 新层上方间距为零;原插入位置那层的历史间距随其一起后移,不被新层吞掉。 offsets.insert(row, 0.0); nmLayerGeometryRow layer; // 待插入层;名称、厚度和颜色先赋值,顶底深度交给重排计算。 layer.m_sName = name; layer.m_dThickness = thickness; layer.m_clrLayer = color; copy.m_vecLayers.insert(row, layer); if (!copy.relayout(m_vecLayers.first().m_dTopDepth, offsets)) return false; *this = copy; return true; } bool nmLayerGeometry::removeLayer(int row) { if (m_vecLayers.size() <= 1 || row < 0 || row >= m_vecLayers.size()) return false; nmLayerGeometry copy(*this); // 删除操作的候选几何,保证失败时原层列表不丢数据。 QVector offsets = gaps(); // 删除前的米制上方间距,合并相邻间隙后与剩余层重新对齐。 // 删除中间层时保留两侧原有间隙;删除首层后仍从原首层顶深开始。 if (row > 0 && row + 1 < offsets.size()) offsets[row + 1] += offsets.at(row); offsets.remove(row); copy.m_vecLayers.remove(row); if (!copy.relayout(m_vecLayers.first().m_dTopDepth, offsets)) return false; *this = copy; return true; }