|
|
#pragma once
|
|
|
|
|
|
#include "DrawOperator/Xy.h"
|
|
|
#include "DrawOperator/one.h"
|
|
|
#include "Util.h"
|
|
|
|
|
|
class CTextBuilder {
|
|
|
public:
|
|
|
CTextBuilder& FontLocation(double x, double y)
|
|
|
{
|
|
|
m_x = x;
|
|
|
m_y = y;
|
|
|
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
CTextBuilder& FontSize(double width, double height)
|
|
|
{
|
|
|
m_width = width;
|
|
|
m_height = height;
|
|
|
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
CTextBuilder& Text(const CString& text)
|
|
|
{
|
|
|
m_text = text;
|
|
|
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
std::unique_ptr<CText> Build() const
|
|
|
{
|
|
|
std::unique_ptr<CText> pText = std::make_unique<CText>();
|
|
|
|
|
|
const CString formatStr = "%lf,%lf,0.00000000 98\n"
|
|
|
"{\n"
|
|
|
"%s\n"
|
|
|
"}\n"
|
|
|
"%lf %lf 0.000000 0 400 0 0 0 1 0 16 0 0 Times New Roman";
|
|
|
|
|
|
CString content;
|
|
|
content.Format(formatStr, m_x, m_y, m_text, m_width, m_height);
|
|
|
|
|
|
CMemFile memFile((BYTE*)content.GetString(), content.GetLength());
|
|
|
pText->Read(memFile, CUR_VERSION);
|
|
|
|
|
|
return pText;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
double m_x = 0.0;
|
|
|
double m_y = 0.0;
|
|
|
double m_width = 21.0;
|
|
|
double m_height = 52.0;
|
|
|
CString m_text;
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 图例 Item
|
|
|
*/
|
|
|
class LegendItem
|
|
|
{
|
|
|
public:
|
|
|
LegendItem(CXy& xy, CLayer* pLayer)
|
|
|
: m_sourceXy(xy), m_pLayer(pLayer)
|
|
|
{
|
|
|
assert(m_pLayer != nullptr);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取图例名称
|
|
|
*
|
|
|
* \return
|
|
|
*/
|
|
|
const CString GetName() const
|
|
|
{
|
|
|
return Path::GetFileNameWithoutExtension(m_pLayer->GetName());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置图例占用范围
|
|
|
*
|
|
|
* \param x 左下角 x 坐标
|
|
|
* \param y 左下角 y 坐标
|
|
|
* \param width 宽
|
|
|
* \param height 高
|
|
|
*/
|
|
|
void SetBounds(double x, double y, double width, double height)
|
|
|
{
|
|
|
m_x = x;
|
|
|
m_y = y;
|
|
|
m_width = width;
|
|
|
m_height = height;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取图例占用范围
|
|
|
*
|
|
|
* \param x 左下角 x 坐标
|
|
|
* \param y 左下角 y 坐标
|
|
|
* \param width 宽
|
|
|
* \param height 高
|
|
|
*/
|
|
|
void GetBounds(double& x, double& y, double& width, double& height) const
|
|
|
{
|
|
|
x = m_x;
|
|
|
y = m_y;
|
|
|
width = m_width;
|
|
|
height = m_height;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成图例
|
|
|
*
|
|
|
* \param targetXy 要生成到的图件 xy
|
|
|
*/
|
|
|
void Build(CXy& targetXy) const
|
|
|
{
|
|
|
BuildCurveRange(targetXy);
|
|
|
BuildElement(targetXy);
|
|
|
BuildName(targetXy);
|
|
|
}
|
|
|
private:
|
|
|
/**
|
|
|
* 生成图例名称
|
|
|
*
|
|
|
* \param targetXy 要生成的目标图件
|
|
|
*/
|
|
|
void BuildName(CXy& targetXy) const
|
|
|
{
|
|
|
CString name = GetName();
|
|
|
auto [x, y] = MeasureNameLocation(name);
|
|
|
|
|
|
std::unique_ptr<CText> pText = CTextBuilder()
|
|
|
.FontLocation(x, y)
|
|
|
.FontSize(10, 26)
|
|
|
.Text(name)
|
|
|
.Build();
|
|
|
|
|
|
CLayer* pLayer = targetXy.FindAddLayer(_T("图例"));
|
|
|
|
|
|
std::unique_ptr<COne> pOne = std::make_unique<COne>();
|
|
|
pOne->SetLayer(pLayer);
|
|
|
pOne->SetValueSafe(pText.release());
|
|
|
pOne->SetColor(RGB(0, 0, 0));
|
|
|
|
|
|
targetXy.AddTailOne(pOne.release());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取第一个点或曲线,如果先拿到点就返回点,先拿到曲线就返回曲线
|
|
|
*
|
|
|
* \return 成功返回点或线的 COne 指针,失败返回 nullptr
|
|
|
*/
|
|
|
COne* GetFirstPointOrCurve() const
|
|
|
{
|
|
|
CPositionList select;
|
|
|
m_sourceXy.GetElement(m_pLayer, select);
|
|
|
|
|
|
for (POSITION pos = select.GetHeadPosition(); pos != nullptr; select.GetNext(pos))
|
|
|
{
|
|
|
POSITION pt = select.GetAt(pos);
|
|
|
COne* pOne = m_sourceXy.GetAt(pt);
|
|
|
|
|
|
if (pOne->GetType() == DOUBLEFOX_POINT || pOne->GetType() == DOUBLEFOX_CURVE)
|
|
|
{
|
|
|
return pOne;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成图例内容
|
|
|
*
|
|
|
* \param targetXy
|
|
|
*/
|
|
|
void BuildElement(CXy& targetXy) const
|
|
|
{
|
|
|
COne* pOne = GetFirstPointOrCurve();
|
|
|
if (pOne == nullptr)
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
std::unique_ptr<COne> pNewOne = std::make_unique<COne>();
|
|
|
*pNewOne = *pOne;
|
|
|
|
|
|
CRect8 oldRange;
|
|
|
oldRange.SetRect(1e100, -1e100, -1e100, 1e100);
|
|
|
pOne->GetRange(oldRange);
|
|
|
|
|
|
if (pOne->GetType() == DOUBLEFOX_POINT) // 如果是点,将它放在新区域的中心
|
|
|
{
|
|
|
CPointNameEx* pPoint = pNewOne->GetValueSafe<CPointNameEx>();
|
|
|
assert(pPoint != nullptr);
|
|
|
|
|
|
CenterPointInRange(*pPoint, CanvasRect());
|
|
|
}
|
|
|
else if (pOne->GetType() == DOUBLEFOX_CURVE) // 如果是曲线,将曲线缩小并移到新区域中
|
|
|
{
|
|
|
CCurveEx* pCurve = pNewOne->GetValueSafe<CCurveEx>();
|
|
|
assert(pCurve != nullptr);
|
|
|
|
|
|
CenterCurveInRange(*pCurve, CanvasRect());
|
|
|
}
|
|
|
|
|
|
CLayer* pLayer = targetXy.FindAddLayer(m_pLayer->GetPathName());
|
|
|
CopyLayerHowtoViewTo(m_pLayer, pLayer);
|
|
|
pNewOne->SetLayer(pLayer);
|
|
|
pNewOne->SetColor(RGB(0, 0, 0));
|
|
|
|
|
|
targetXy.AddTailOne(pNewOne.release());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将点放到目标区域中心
|
|
|
*
|
|
|
* \param point
|
|
|
* \param range
|
|
|
*/
|
|
|
void CenterPointInRange(CPointNameEx& point, const CRect8& range) const
|
|
|
{
|
|
|
point.x0 = range.left + (range.right - range.left) / 2;
|
|
|
point.y0 = range.bottom + (range.top - range.bottom) / 2;
|
|
|
point.SetName(""); // 清空点的名称
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将曲线放到目标区域中心
|
|
|
*
|
|
|
* \param curve
|
|
|
* \param range
|
|
|
*/
|
|
|
void CenterCurveInRange(CCurveEx& curve, const CRect8& range) const
|
|
|
{
|
|
|
CRect8 oldRange;
|
|
|
oldRange.SetRect(1e100, -1e100, -1e100, 1e100);
|
|
|
curve.GetRange(oldRange);
|
|
|
|
|
|
CRect8 newRange = ScaleRect(range, 0.9);
|
|
|
|
|
|
for (int i = 0; i < curve.num; i++)
|
|
|
{
|
|
|
auto [x, y] = ScalePoint(curve.x[i], curve.y[i], oldRange, newRange);
|
|
|
curve.x[i] = x;
|
|
|
curve.y[i] = y;
|
|
|
}
|
|
|
curve.GetLocation();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 绘制图例包围线
|
|
|
*
|
|
|
* \param targetXy
|
|
|
*/
|
|
|
void BuildCurveRange(CXy& targetXy) const
|
|
|
{
|
|
|
std::unique_ptr<CCurveEx> pCurve = std::make_unique<CCurveEx>();
|
|
|
pCurve->Create(5);
|
|
|
|
|
|
// 将最下面的 y 向上提升
|
|
|
double y = m_y + m_height * (1 - m_canvasHeightRatio);
|
|
|
|
|
|
pCurve->x[0] = m_x;
|
|
|
pCurve->y[0] = y;
|
|
|
|
|
|
pCurve->x[1] = m_x + m_width;
|
|
|
pCurve->y[1] = y;
|
|
|
|
|
|
pCurve->x[2] = m_x + m_width;
|
|
|
pCurve->y[2] = m_y + m_height;
|
|
|
|
|
|
pCurve->x[3] = m_x;
|
|
|
pCurve->y[3] = m_y + m_height;
|
|
|
|
|
|
pCurve->x[4] = m_x;
|
|
|
pCurve->y[4] = y;
|
|
|
|
|
|
pCurve->GetLocation();
|
|
|
|
|
|
CLayer* pLayer = targetXy.FindAddLayer(_T("图例"));
|
|
|
|
|
|
std::unique_ptr<COne> pOne = std::make_unique<COne>();
|
|
|
pOne->SetValueSafe(pCurve.release());
|
|
|
pOne->SetLayer(pLayer);
|
|
|
pOne->SetColor(RGB(0, 0, 0));
|
|
|
|
|
|
targetXy.AddTailOne(pOne.release());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将点从一个矩形范围缩放到另一个矩形范围
|
|
|
*
|
|
|
* \param x x 坐标
|
|
|
* \param y y 坐标
|
|
|
* \param oldRange 旧的范围
|
|
|
* \param newRange 新的范围
|
|
|
* \return 缩放后的 x y 坐标
|
|
|
*/
|
|
|
std::pair<double, double> ScalePoint(double x, double y, const CRect8& oldRange, const CRect8& newRange) const
|
|
|
{
|
|
|
double xMin = oldRange.left;
|
|
|
double xMax = oldRange.right;
|
|
|
double yMin = oldRange.bottom;
|
|
|
double yMax = oldRange.top;
|
|
|
|
|
|
double newXMin = newRange.left;
|
|
|
double newXMax = newRange.right;
|
|
|
double newYMin = newRange.bottom;
|
|
|
double newYMax = newRange.top;
|
|
|
|
|
|
double newX = ((x - xMin) / (xMax - xMin)) * (newXMax - newXMin) + newXMin;
|
|
|
double newY = ((y - yMin) / (yMax - yMin)) * (newYMax - newYMin) + newYMin;
|
|
|
|
|
|
return { newX, newY };
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取图例绘制区域
|
|
|
*
|
|
|
* \return
|
|
|
*/
|
|
|
CRect8 CanvasRect() const
|
|
|
{
|
|
|
return CRect8(m_x, m_y + m_height, m_x + m_width, m_y + m_height * (1 - m_canvasHeightRatio));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将矩形向中心缩放
|
|
|
*
|
|
|
* \param rect 要缩放的矩形
|
|
|
* \param scaleFactor 缩放系数
|
|
|
* \return
|
|
|
*/
|
|
|
CRect8 ScaleRect(const CRect8& rect, double scaleFactor) const
|
|
|
{
|
|
|
assert(scaleFactor > 0);
|
|
|
|
|
|
double width = rect.right - rect.left;
|
|
|
double height = rect.top - rect.bottom;
|
|
|
|
|
|
double newWidth = width * scaleFactor;
|
|
|
double newHeight = height * scaleFactor;
|
|
|
|
|
|
double left = rect.left + (width - newWidth) / 2;
|
|
|
double top = rect.top - (height - newHeight) / 2;
|
|
|
double right = rect.right - (width - newWidth) / 2;
|
|
|
double bottom = rect.bottom + (height - newHeight) / 2;
|
|
|
return CRect8(left, top, right, bottom);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 测量名称该显示的 x, y 位置
|
|
|
*
|
|
|
* \param text
|
|
|
* \return
|
|
|
*/
|
|
|
std::pair<double, double> MeasureNameLocation(const CString& name) const
|
|
|
{
|
|
|
double x = m_x + m_width / 2; // 要根据文字的宽度计算 x 的位置,目前把 x 设置到中间位置,貌似文本就显示在中间
|
|
|
double height = m_height * (1 - m_canvasHeightRatio); // 这是名字显示区域高度
|
|
|
double y = m_y + height; // y 抬升,这个位置是我们留给文字显示的区域最高处
|
|
|
y -= height * m_nameOffsetRatio; // 再移动到文字显示位置
|
|
|
return { x, y };
|
|
|
}
|
|
|
|
|
|
// 由于我们大地坐标是笛卡尔坐标系,所以这里的 x y 是左下角位置,这是行业一般惯例
|
|
|
double m_x = 0.0; // 左下 x
|
|
|
double m_y = 0.0; // 左下 y
|
|
|
double m_width = 2.0; // 宽度
|
|
|
double m_height = 1.0; // 高度
|
|
|
double m_nameOffsetRatio = 7.0 / 52.0; // 名称显示位置,相对于名称高度占比
|
|
|
double m_canvasHeightRatio = 65.0 / 117.0; // 绘制区域高度和整个高度的比例
|
|
|
CXy& m_sourceXy; // 原图件xy,即要生成符号的图件 xy
|
|
|
CLayer* m_pLayer = nullptr; // 要生成符号的图层
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 图例
|
|
|
*
|
|
|
*/
|
|
|
class Legend
|
|
|
{
|
|
|
public:
|
|
|
/**
|
|
|
* 构造函数
|
|
|
*
|
|
|
* \param title 标题
|
|
|
* \param columns 多少列
|
|
|
* \param layers 哪些图层要生成图例
|
|
|
*/
|
|
|
Legend(CXy &xy, double x, double y, double width, const CString& title, int columns, const std::vector<CString>& layers = {})
|
|
|
: m_sourceXy(xy), m_x(x), m_y(y), m_width(width), m_title(title), m_columns(columns), m_layers(layers)
|
|
|
{
|
|
|
for (const CString& layer : layers)
|
|
|
{
|
|
|
CLayer* pLayer = m_sourceXy.FindLayer(layer);
|
|
|
if (pLayer != nullptr)
|
|
|
{
|
|
|
m_boxes.emplace_back(m_sourceXy, pLayer);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 构造图例
|
|
|
*
|
|
|
* \return 返回构造好的图例指针
|
|
|
*/
|
|
|
std::unique_ptr<CInsertBlock> Build()
|
|
|
{
|
|
|
ComputeLayout();
|
|
|
|
|
|
std::unique_ptr<CXy> pNewXy = std::make_unique<CXy>();
|
|
|
pNewXy->color = 0;
|
|
|
|
|
|
// 生成图例边框
|
|
|
BuildCurveRange(*pNewXy);
|
|
|
|
|
|
// 生成图例标题
|
|
|
BuildTitle(*pNewXy);
|
|
|
|
|
|
// 生成图例 item
|
|
|
for (auto& box : m_boxes)
|
|
|
{
|
|
|
box.Build(*pNewXy);
|
|
|
}
|
|
|
|
|
|
// 创建 Block
|
|
|
std::unique_ptr<CInsertBlock> pBlock = std::make_unique<CInsertBlock>();
|
|
|
pBlock->SetXy(pNewXy.release());
|
|
|
|
|
|
return pBlock;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 计算每个图例占的空间
|
|
|
*
|
|
|
*/
|
|
|
void ComputeLayout()
|
|
|
{
|
|
|
size_t rows = m_boxes.size() / m_columns + (m_boxes.size() % m_columns == 0 ? 0 : 1);
|
|
|
size_t columns = m_columns;
|
|
|
|
|
|
// 计算出列间隙宽度
|
|
|
double horizontalSpacing = CalcHorizontalSpacing(m_width, static_cast<int>(columns), m_boxWidthSpacingRatio);
|
|
|
|
|
|
// 计算图例的宽度
|
|
|
m_boxWidth = horizontalSpacing * m_boxWidthSpacingRatio;
|
|
|
// 计算图例的高度,设计宽高比为 10 比 8
|
|
|
m_boxHeight = m_boxWidth * m_widthHeightRatio;
|
|
|
|
|
|
m_titleHeight = m_boxWidth * m_titleWidthRatio;
|
|
|
|
|
|
// 计算图例高度
|
|
|
m_height = m_boxHeight * rows + m_titleHeight;
|
|
|
|
|
|
for (size_t i = 0; i < m_boxes.size(); i++)
|
|
|
{
|
|
|
size_t row = i / columns;
|
|
|
size_t column = i % columns;
|
|
|
|
|
|
// x 偏移量由前面的列宽加列间隙组成
|
|
|
double xOffset = column * m_boxWidth + (column + 1) * horizontalSpacing;
|
|
|
double yOffset = (row + 1) * m_boxHeight;
|
|
|
|
|
|
// y 则麻烦的多
|
|
|
// 我们先要计算出图例框最高的位置
|
|
|
// 然后减去标题高度
|
|
|
// 再向下减去具体的位置
|
|
|
// 由于我们要计算的 y 是方框左下角的位置,所以还要再减上 boxHeight
|
|
|
yOffset = m_height - yOffset - m_titleHeight;
|
|
|
|
|
|
m_boxes[i].SetBounds(xOffset, yOffset, m_boxWidth, m_boxHeight);
|
|
|
}
|
|
|
|
|
|
OffsetBoxes();
|
|
|
}
|
|
|
|
|
|
double GetWidth() const
|
|
|
{
|
|
|
return m_width;
|
|
|
}
|
|
|
|
|
|
double GetHeight() const
|
|
|
{
|
|
|
return m_height;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
/**
|
|
|
* 前面的计算是基于 0,0 为左下坐标开始计算的,现在要根本 m_x, m_y 坐标进行偏移
|
|
|
*
|
|
|
*/
|
|
|
void OffsetBoxes()
|
|
|
{
|
|
|
for (auto& box : m_boxes)
|
|
|
{
|
|
|
double x = 0.0;
|
|
|
double y = 0.0;
|
|
|
double width = 0.0;
|
|
|
double height = 0.0;
|
|
|
box.GetBounds(x, y, width, height);
|
|
|
box.SetBounds(m_x + x, m_y + y, width, height);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 将笛卡尔坐标系的矩形向中心缩小
|
|
|
std::tuple<double, double, double, double> ScaleRect(double x, double y, double width, double height, double scaleFactor)
|
|
|
{
|
|
|
assert(scaleFactor > 0 && scaleFactor <= 1);
|
|
|
|
|
|
double newWidth = width * scaleFactor;
|
|
|
double newHeight = width * scaleFactor;
|
|
|
|
|
|
double newX = x + ((width - newWidth) / 2);
|
|
|
double newY = y + ((height - newHeight) / 2);
|
|
|
|
|
|
return { newX, newY, newWidth, newHeight };
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 绘制最外围的曲线
|
|
|
*
|
|
|
* \param targetXy 要绘制的目标图件
|
|
|
*/
|
|
|
void BuildCurveRange(CXy& targetXy) const
|
|
|
{
|
|
|
std::unique_ptr<CCurveEx> pCurve = std::make_unique<CCurveEx>();
|
|
|
pCurve->Create(5);
|
|
|
|
|
|
pCurve->x[0] = m_x;
|
|
|
pCurve->y[0] = m_y;
|
|
|
|
|
|
pCurve->x[1] = m_x + m_width;
|
|
|
pCurve->y[1] = m_y;
|
|
|
|
|
|
pCurve->x[2] = m_x + m_width;
|
|
|
pCurve->y[2] = m_y + m_height;
|
|
|
|
|
|
pCurve->x[3] = m_x;
|
|
|
pCurve->y[3] = m_y + m_height;
|
|
|
|
|
|
pCurve->x[4] = m_x;
|
|
|
pCurve->y[4] = m_y;
|
|
|
|
|
|
pCurve->GetLocation();
|
|
|
|
|
|
CLayer* pLayer = targetXy.FindAddLayer(_T("图例"));
|
|
|
|
|
|
std::unique_ptr<COne> pOne = std::make_unique<COne>();
|
|
|
pOne->SetValueSafe(pCurve.release());
|
|
|
pOne->SetLayer(pLayer);
|
|
|
pOne->HowToViewCurve = CreateBorderHowToView().release();
|
|
|
|
|
|
targetXy.AddTailOne(pOne.release());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成“图例”这个标题
|
|
|
*
|
|
|
* \param targetXy 要绘制的目标图件
|
|
|
*/
|
|
|
void BuildTitle(CXy& targetXy) const
|
|
|
{
|
|
|
double x = m_x + m_width * 0.5;
|
|
|
double y = m_y + m_height - m_titleHeight * m_titleOffsetRatio;
|
|
|
|
|
|
std::unique_ptr<CText> pText = CTextBuilder()
|
|
|
.FontLocation(x, y)
|
|
|
.FontSize(21, 52)
|
|
|
.Text("图 例")
|
|
|
.Build();
|
|
|
|
|
|
CLayer* pLayer = targetXy.FindAddLayer(_T("图例"));
|
|
|
|
|
|
std::unique_ptr<COne> pOne = std::make_unique<COne>();
|
|
|
pOne->SetLayer(pLayer);
|
|
|
pOne->SetValueSafe(pText.release());
|
|
|
pOne->SetColor(RGB(0, 0, 0));
|
|
|
|
|
|
targetXy.AddTailOne(pOne.release());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 添加块到原图件
|
|
|
*
|
|
|
* \param pBlock 组合后的图例块
|
|
|
*/
|
|
|
void AddBlock(std::unique_ptr<CInsertBlock> pBlock)
|
|
|
{
|
|
|
std::unique_ptr<COne> pOne = std::make_unique<COne>();
|
|
|
CLayer *pLayer = m_sourceXy.FindAddLayer(_T("图例"));
|
|
|
pOne->SetValueSafe(pBlock.release());
|
|
|
pOne->SetLayer(pLayer);
|
|
|
m_sourceXy.AddTailOne(pOne.release());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 创建图例最外围边框线的修饰
|
|
|
*
|
|
|
* \return
|
|
|
*/
|
|
|
std::unique_ptr<CHowToViewCurve> CreateBorderHowToView() const
|
|
|
{
|
|
|
std::unique_ptr<CCurvePropertiesEx> prop1 = std::make_unique<CCurvePropertiesEx>();
|
|
|
prop1->m_size = 0;
|
|
|
prop1->color = abs(16777215);
|
|
|
prop1->m_nFlags = 65568;
|
|
|
prop1->m_SmoothStep = 0;
|
|
|
prop1->m_offset = 0;
|
|
|
prop1->m_stuCurveWave.T = 0;
|
|
|
prop1->m_stuCurveWave.T = 0;
|
|
|
prop1->m_stuCurveWave.A = 0;
|
|
|
prop1->m_stuCurveWave.dnum = 0;
|
|
|
|
|
|
std::unique_ptr<CCurvePropertiesEx> prop2 = std::make_unique<CCurvePropertiesEx>();
|
|
|
prop2->m_size = 0;
|
|
|
prop2->color = abs(0);
|
|
|
prop2->m_nFlags = 65536;
|
|
|
prop2->m_SmoothStep = 0;
|
|
|
prop2->m_offset = 0;
|
|
|
prop2->m_stuCurveWave.T = 0;
|
|
|
prop2->m_stuCurveWave.T = 0;
|
|
|
prop2->m_stuCurveWave.A = 0;
|
|
|
prop2->m_stuCurveWave.dnum = 0;
|
|
|
|
|
|
std::unique_ptr<CHowToViewCurve> pHowToViewCurve = std::make_unique<CHowToViewCurve>();
|
|
|
pHowToViewCurve->Add(prop1.release());
|
|
|
pHowToViewCurve->Add(prop2.release());
|
|
|
|
|
|
return pHowToViewCurve;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据 总宽度,列数,列宽和间隙比例 计算间隙宽度
|
|
|
*
|
|
|
* \param width 宽度
|
|
|
* \param n 多少列
|
|
|
* \param ratio 列和间隙的宽度比例
|
|
|
* \return
|
|
|
*/
|
|
|
double CalcHorizontalSpacing(double width, int n, double ratio) const
|
|
|
{
|
|
|
// 总共 n 列,间隙为 n + 1 列
|
|
|
// (n + 1) * space + n * boxWidth = width
|
|
|
// boxWidth = ratio * space
|
|
|
// (n + 1) * space + n * (ratio * space) = width
|
|
|
// (n + 1 + n * ratio) * space = width
|
|
|
// space = width / (n + 1 + n * ratio)
|
|
|
|
|
|
return width / (n + 1 + n * ratio);
|
|
|
}
|
|
|
|
|
|
// 由于我们大地坐标是笛卡尔坐标系,所以这里的 x y 是左下角位置,这是行业一般惯例
|
|
|
double m_x = 0.0;
|
|
|
double m_y = 0.0;
|
|
|
double m_width = 512.0;
|
|
|
double m_height = 421.0;
|
|
|
CString m_title;
|
|
|
// 每列间隔和图例 Item 宽度为 46 比 106
|
|
|
double m_boxWidthSpacingRatio = 106.0 / 46.0;
|
|
|
// 图列宽高比
|
|
|
double m_widthHeightRatio = 106.0 / 117.0;
|
|
|
// 标题栏高度与图例宽度的比例
|
|
|
double m_titleWidthRatio = 77.0 / 106.0;
|
|
|
// 标题起始位置相对整个标题框高度占比
|
|
|
double m_titleOffsetRatio = 10.0 / 77.0;
|
|
|
double m_boxWidth = 0.0;
|
|
|
double m_boxHeight = 0.0;
|
|
|
double m_titleHeight = 0.0;
|
|
|
int m_columns = 3;
|
|
|
std::vector<CString> m_layers;
|
|
|
std::vector<LegendItem> m_boxes;
|
|
|
CXy& m_sourceXy;
|
|
|
}; |