You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.7 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma once
#include "ItemCurve.h"
#include <vector>
#include <utility>
/**
* @brief 样条曲线数据模型
*
* 控制点为唯一数据源通过段元数据生成渲染点。n 个控制点对应 n-1 段,
* 渲染点带段索引用于绘制和命中检测,最终通过 ToCCurveEx() 输出供文档使用。
*/
/** @brief 段来源Algorithm 由 Catmull-Rom 计算Original 使用存储点 */
enum class SegmentSource
{
Algorithm,
Original
};
/** @brief 段元数据,存储该段采样点 */
struct SegmentMeta
{
SegmentSource source = SegmentSource::Algorithm;
std::vector<dfPoint> points;
};
/** @brief 渲染点,带所属段索引,用于绘制和命中检测 */
struct RenderPointWithSeg
{
dfPoint point;
int segIndex;
};
/**
* @brief 样条曲线模型
*
* 数据流:控制点 → 段元数据 → 渲染点缓存 → CCurveEx
*/
class SplineCurveModel
{
public:
explicit SplineCurveModel(int samplesPerSegment = 30);
const std::vector<dfPoint>& GetControlPoints() const;
std::vector<dfPoint>& GetControlPoints();
const std::vector<SegmentMeta>& GetSegmentMeta() const;
const std::vector<RenderPointWithSeg>& GetRenderPoints() const;
int GetControlPointCount() const;
int GetSegmentCount() const;
bool IsValid() const;
/**
* @brief 在 index 处插入控制点,返回新索引,非法返回 -1
*/
int InsertControlPoint(int index, const dfPoint& point);
/**
* @brief 删除 index 处控制点,至少保留 3 个,返回是否成功
*/
bool RemoveControlPoint(int index);
/** @brief 更新控制点坐标,仅重算受影响的段 */
void UpdateControlPoint(int index, double x, double y);
/**
* @brief 重建渲染点缓存
* @param affectedRange 仅重建此范围nullptr 表示全量
*/
void RebuildRenderPoints(const std::pair<int, int>* affectedRange = nullptr);
/**
* @brief 从模型生成 CCurveEx供文档/绘制使用
*/
void ToCCurveEx(CCurveEx& outCurve) const;
/**
* @brief 从 CCurveEx 初始化模型,使用 SplineAlgorithm 抽稀生成控制点
*/
bool InitFromCCurveEx(const CCurveEx& curve, double splineStep);
int GetSamplesPerSegment() const;
void SetSamplesPerSegment(int n);
private:
std::vector<dfPoint> m_controlPoints;
std::vector<SegmentMeta> m_segmentMeta;
std::vector<RenderPointWithSeg> m_renderPoints;
int m_samplesPerSegment;
void InitSegmentMeta();
/**
* Catmull-Rom 单段采样4 控制点 p0..p3绘制 p1→p2
*/
static std::vector<dfPoint> SampleSegment(
const dfPoint& p0, const dfPoint& p1, const dfPoint& p2, const dfPoint& p3,
int samplesPerSegment);
static std::pair<int, int> GetAffectedSegmentRange(int controlPointIndex, int segmentCount);
};