#include "StdAfx.h" #include "SplineCurveModel.h" #include "SplineAlgorithm.h" #include #include namespace { bool IsFinitePoint(const dfPoint& p) { return std::isfinite(p.x0) && std::isfinite(p.y0); } bool IsDegenerateSegment(const dfPoint& p0, const dfPoint& p1, const dfPoint& p2, const dfPoint& p3) { const double eps = 1e-12; auto dist2 = [](const dfPoint& a, const dfPoint& b) { double dx = a.x0 - b.x0; double dy = a.y0 - b.y0; return dx * dx + dy * dy; }; return dist2(p1, p2) <= eps; } /** Catmull-Rom 插值:t∈[0,1] 返回 p1→p2 段上参数 t 处的点 */ dfPoint CatmullRom(const dfPoint& p0, const dfPoint& p1, const dfPoint& p2, const dfPoint& p3, double t) { const double t2 = t * t; const double t3 = t2 * t; dfPoint out; out.x0 = 0.5 * ( (2 * p1.x0) + (-p0.x0 + p2.x0) * t + (2 * p0.x0 - 5 * p1.x0 + 4 * p2.x0 - p3.x0) * t2 + (-p0.x0 + 3 * p1.x0 - 3 * p2.x0 + p3.x0) * t3); out.y0 = 0.5 * ( (2 * p1.y0) + (-p0.y0 + p2.y0) * t + (2 * p0.y0 - 5 * p1.y0 + 4 * p2.y0 - p3.y0) * t2 + (-p0.y0 + 3 * p1.y0 - 3 * p2.y0 + p3.y0) * t3); out.z0 = p1.z0; out.l = p1.l; out.no = p1.no; return out; } } SplineCurveModel::SplineCurveModel(int samplesPerSegment) : m_samplesPerSegment(samplesPerSegment > 0 ? samplesPerSegment : 30) { } const std::vector& SplineCurveModel::GetControlPoints() const { return m_controlPoints; } std::vector& SplineCurveModel::GetControlPoints() { return m_controlPoints; } const std::vector& SplineCurveModel::GetSegmentMeta() const { return m_segmentMeta; } const std::vector& SplineCurveModel::GetRenderPoints() const { return m_renderPoints; } int SplineCurveModel::GetControlPointCount() const { return static_cast(m_controlPoints.size()); } int SplineCurveModel::GetSegmentCount() const { return static_cast(m_segmentMeta.size()); } bool SplineCurveModel::IsValid() const { return GetControlPointCount() >= 2; } int SplineCurveModel::GetSamplesPerSegment() const { return m_samplesPerSegment; } void SplineCurveModel::SetSamplesPerSegment(int n) { m_samplesPerSegment = (n > 0) ? n : 1; } std::vector SplineCurveModel::SampleSegment( const dfPoint& p0, const dfPoint& p1, const dfPoint& p2, const dfPoint& p3, int samplesPerSegment) { if (IsDegenerateSegment(p0, p1, p2, p3)) return { p1, p2 }; std::vector result; result.reserve(samplesPerSegment + 1); for (int j = 0; j <= samplesPerSegment; ++j) { const double t = static_cast(j) / samplesPerSegment; dfPoint dp = CatmullRom(p0, p1, p2, p3, t); if (!IsFinitePoint(dp)) dp = (j <= samplesPerSegment / 2) ? p1 : p2; result.push_back(dp); } return result; } void SplineCurveModel::InitSegmentMeta() { const int n = static_cast(m_controlPoints.size()); m_segmentMeta.clear(); m_segmentMeta.reserve((std::max)(0, n - 1)); for (int i = 0; i < n - 1; ++i) m_segmentMeta.push_back({ SegmentSource::Algorithm, {} }); } std::pair SplineCurveModel::GetAffectedSegmentRange(int controlPointIndex, int segmentCount) { /* Catmull-Rom 局部支撑:控制点 i 影响段 i-2, i-1, i, i+1 */ const int start = (std::max)(0, controlPointIndex - 2); const int end = (std::min)(segmentCount - 1, controlPointIndex + 1); return { start, end }; } int SplineCurveModel::InsertControlPoint(int index, const dfPoint& point) { const int n = static_cast(m_controlPoints.size()); if (index < 0 || index > n) return -1; m_controlPoints.insert(m_controlPoints.begin() + index, point); const int segToSplit = index - 1; if (segToSplit >= 0 && segToSplit < static_cast(m_segmentMeta.size())) { SegmentMeta& meta = m_segmentMeta[segToSplit]; meta.source = SegmentSource::Algorithm; meta.points.clear(); } m_segmentMeta.insert(m_segmentMeta.begin() + index, { SegmentSource::Algorithm, {} }); const int nSeg = static_cast(m_segmentMeta.size()); const auto range = GetAffectedSegmentRange(index, nSeg); // 将受影响范围内的 Original 段标记为 Algorithm for (int i = range.first; i <= range.second; ++i) { if (i >= 0 && i < nSeg && m_segmentMeta[i].source == SegmentSource::Original) { m_segmentMeta[i].source = SegmentSource::Algorithm; m_segmentMeta[i].points.clear(); } } RebuildRenderPoints(&range); return index; } bool SplineCurveModel::RemoveControlPoint(int index) { const int n = static_cast(m_controlPoints.size()); if (n <= 3 || index < 0 || index >= n) return false; m_controlPoints.erase(m_controlPoints.begin() + index); const int leftIdx = index - 1; const int rightIdx = index; if (leftIdx >= 0 && rightIdx < static_cast(m_segmentMeta.size())) { // 删除控制点后,合并的段一律用算法重建 m_segmentMeta[leftIdx].source = SegmentSource::Algorithm; m_segmentMeta[leftIdx].points.clear(); } m_segmentMeta.erase(m_segmentMeta.begin() + index); // 将受影响范围内的 Original 段标记为 Algorithm const int nSeg = static_cast(m_segmentMeta.size()); if (nSeg > 0) { const int effectiveIndex = (std::min)(index, nSeg - 1); const auto range = GetAffectedSegmentRange(effectiveIndex, nSeg); for (int i = range.first; i <= range.second; ++i) { if (i >= 0 && i < nSeg && m_segmentMeta[i].source == SegmentSource::Original) { m_segmentMeta[i].source = SegmentSource::Algorithm; m_segmentMeta[i].points.clear(); } } } RebuildRenderPoints(); return true; } void SplineCurveModel::UpdateControlPoint(int index, double x, double y) { const int n = static_cast(m_controlPoints.size()); if (index < 0 || index >= n) return; m_controlPoints[index].x0 = x; m_controlPoints[index].y0 = y; const int nSeg = static_cast(m_segmentMeta.size()); const auto range = GetAffectedSegmentRange(index, nSeg); // 将受影响范围内的 Original 段标记为 Algorithm,使其被 Catmull-Rom 重新计算 for (int i = range.first; i <= range.second; ++i) { if (i >= 0 && i < nSeg && m_segmentMeta[i].source == SegmentSource::Original) { m_segmentMeta[i].source = SegmentSource::Algorithm; m_segmentMeta[i].points.clear(); } } RebuildRenderPoints(&range); } void SplineCurveModel::RebuildRenderPoints(const std::pair* affectedRange) { const int n = static_cast(m_controlPoints.size()); const int nSeg = n - 1; if (nSeg <= 0) { m_renderPoints.clear(); return; } int startSeg = 0; int endSeg = nSeg - 1; if (affectedRange) { startSeg = (std::max)(0, affectedRange->first); endSeg = (std::min)(nSeg - 1, affectedRange->second); } auto cp = [this, n](int i) -> const dfPoint& { i = (std::max)(0, (std::min)(n - 1, i)); return m_controlPoints[i]; }; for (int i = startSeg; i <= endSeg; ++i) { if (i < 0 || i >= nSeg) continue; SegmentMeta& meta = m_segmentMeta[i]; if (meta.source == SegmentSource::Algorithm) { const dfPoint p0 = cp(i - 1); const dfPoint p1 = cp(i); const dfPoint p2 = cp(i + 1); const dfPoint p3 = cp(i + 2); meta.points = SampleSegment(p0, p1, p2, p3, m_samplesPerSegment); } } m_renderPoints.clear(); for (int i = 0; i < nSeg; ++i) { for (const auto& pt : m_segmentMeta[i].points) m_renderPoints.push_back({ pt, i }); } } void SplineCurveModel::ToCCurveEx(CCurveEx& outCurve) const { CPointList pl; for (const auto& rp : m_renderPoints) pl.AddTail(rp.point); if (pl.IsEmpty()) return; outCurve.SetPoints(pl, 4, TRUE); } bool SplineCurveModel::InitFromCCurveEx(const CCurveEx& curve, double splineStep) { if (curve.num < 2) return false; m_controlPoints.clear(); m_segmentMeta.clear(); m_renderPoints.clear(); // 保持原始曲线拷贝,用于提取段数据 CCurveEx origCurve = curve; CCurveEx tempInput = curve; CCurveEx simplifiedOutput; double errorThreshold = splineStep; SplineAlgorithm::SimplifyCurve(tempInput, errorThreshold, 0, simplifiedOutput); if (simplifiedOutput.num < 2) return false; dfPoint pt; for (int i = 0; i < simplifiedOutput.num; ++i) { simplifiedOutput.GetPoint(i, pt); m_controlPoints.push_back(pt); } // 若调用方给出了期望步长,则根据抽稀后的控制点估算一个合适的每段采样数 if (splineStep > 0.0 && m_controlPoints.size() >= 2) { double totalLen = 0.0; for (size_t i = 1; i < m_controlPoints.size(); ++i) { const dfPoint& a = m_controlPoints[i - 1]; const dfPoint& b = m_controlPoints[i]; const double dx = b.x0 - a.x0; const double dy = b.y0 - a.y0; totalLen += std::sqrt(dx * dx + dy * dy); } const int segCount = static_cast(m_controlPoints.size()) - 1; if (segCount > 0 && totalLen > 0.0) { const double avgLen = totalLen / segCount; int samples = static_cast(std::ceil(avgLen / splineStep)); if (samples < 1) samples = 1; m_samplesPerSegment = samples; } } // 将控制点映射回原始曲线索引,保留原始段的精确点数据 std::vector indices = SplineAlgorithm::FindClosestPointIndices(m_controlPoints, &origCurve); // 确保首尾索引覆盖完整曲线 if (indices.size() >= 2) { indices[0] = 0; indices[indices.size() - 1] = origCurve.num - 1; } const int segCount = static_cast(m_controlPoints.size()) - 1; m_segmentMeta.clear(); m_segmentMeta.reserve(segCount); for (int i = 0; i < segCount; ++i) { SegmentMeta meta; int startIdx = (i < static_cast(indices.size())) ? indices[i] : -1; int endIdx = ((i + 1) < static_cast(indices.size())) ? indices[i + 1] : -1; if (startIdx >= 0 && endIdx >= 0 && endIdx < origCurve.num && startIdx <= endIdx) { meta.source = SegmentSource::Original; dfPoint p; for (int j = startIdx; j <= endIdx; ++j) { origCurve.GetPoint(j, p); meta.points.push_back(p); } } else { // 无法映射的段,用 Catmull-Rom 算法重建 meta.source = SegmentSource::Algorithm; } m_segmentMeta.push_back(std::move(meta)); } // 仅重建 Algorithm 段的渲染点,Original 段已有精确数据 RebuildRenderPoints(nullptr); return true; }