688 lines
53 KiB
Plaintext
688 lines
53 KiB
Plaintext
//+------------------------------------------------------------------+
|
||
//| TestStand3D.mqh |
|
||
//| 3D-панель визуализации для тест-стенда метаэвристик |
|
||
//| |
|
||
//| Порядок #include в скрипте: |
|
||
//| 1. TestStandFunctions.mqh — C_TestStand (2D-стенд) |
|
||
//| 2. TestFunctions.mqh — C_Function, GenerateDataFixedSize|
|
||
//| 3. TestStand3D.mqh — этот файл |
|
||
//| 4. алгоритм оптимизации |
|
||
//+------------------------------------------------------------------+
|
||
#include <Canvas\Canvas3D.mqh>
|
||
#include <Canvas\DX\DXSurface.mqh>
|
||
#include <Math\AOs\TestFunctions.mqh>
|
||
//--- Параметры камеры (можно менять)
|
||
#define S3D_DIST_DEF 35.0 // дистанция по умолчанию
|
||
#define S3D_DIST_MIN 12.0 // минимальный zoom-in
|
||
#define S3D_DIST_MAX 80.0 // максимальный zoom-out
|
||
#define S3D_PITCH_DEF 0.45f // наклон камеры по умолчанию (~26°)
|
||
#define S3D_FOV 0.5236f // поле зрения = π/6 (30°)
|
||
//--- Авто-вращение (активируется после бездействия мыши)
|
||
#define S3D_TIMEOUT 1.0 // секунд бездействия до начала вращения
|
||
#define S3D_AUTO_YAW 0.07 // рад/с вращения по оси Y (~4°/с)
|
||
#define S3D_RETURN 0.02 // коэффициент плавного возврата наклона к дефолту
|
||
//--- Полуразмеры поверхности в мировых координатах
|
||
#define S3D_SX 10.0f
|
||
#define S3D_SY 5.0f
|
||
#define S3D_SZ 10.0f
|
||
//--- Фоновый цвет панели
|
||
#define S3D_BG 0xFF0D0D1A
|
||
//+------------------------------------------------------------------+
|
||
//| S3DTrailPt — точка трека лучшего решения (мировые координаты) |
|
||
//+------------------------------------------------------------------+
|
||
struct S3DTrailPt
|
||
{
|
||
float wx, wy, wz;
|
||
};
|
||
//+------------------------------------------------------------------+
|
||
//| S3DDot — цветная точка проекции функции (мировые координаты) |
|
||
//+------------------------------------------------------------------+
|
||
struct S3DDot
|
||
{
|
||
float wx, wy, wz;
|
||
uint clr; // ARGB
|
||
};
|
||
//+------------------------------------------------------------------+
|
||
//| S3DCentroid — центроид агента (мировые координаты + тип) |
|
||
//+------------------------------------------------------------------+
|
||
struct S3DCentroid
|
||
{
|
||
float wx, wy, wz;
|
||
bool is_best; // true = ao.cB (глобально лучший)
|
||
};
|
||
//+------------------------------------------------------------------+
|
||
//| C_TestStand3D |
|
||
//| |
|
||
//| Отображает левую (H×H) панель тест-стенда как интерактивную |
|
||
//| 3D-поверхность функции с проекцией агентов и треком лучшего. |
|
||
//| |
|
||
//| Управление (EA-контекст, события через OnChartEvent): |
|
||
//| LMB-drag — вращение камеры |
|
||
//| Scroll — zoom |
|
||
//| Double-click — сброс камеры к позиции по умолчанию |
|
||
//| |
|
||
//| В скрипте управление мышью недоступно (синхронный OnStart), |
|
||
//| работает только авто-вращение через OnTimer. |
|
||
//+------------------------------------------------------------------+
|
||
class C_TestStand3D
|
||
{
|
||
//--- DX-объекты
|
||
CCanvas3D m_cv;
|
||
CDXSurface m_surf;
|
||
//--- параметры канваса
|
||
string m_name;
|
||
int m_x, m_y, m_w, m_h;
|
||
bool m_surf_ok;
|
||
bool m_vis;
|
||
bool m_shutdown_done;
|
||
int m_grid;
|
||
//--- параметры камеры
|
||
float m_pitch; // наклон (вокруг X)
|
||
float m_yaw; // азимут (вокруг Y)
|
||
double m_dist; // расстояние от центра сцены
|
||
double m_timeout; // таймер бездействия до авто-вращения
|
||
int m_mx, m_my; // предыдущая позиция мыши (локальная)
|
||
//--- матрица вид×проекция для ручного проецирования агентов на экран
|
||
DXMatrix m_vp;
|
||
//--- диапазон домена функции (для маппинга агентов в мировые координаты)
|
||
double m_fxMin, m_fxMax, m_fyMin, m_fyMax;
|
||
//--- цветные точки проекций (agentCount * funcCount штук)
|
||
S3DDot m_dots [];
|
||
int m_nDots;
|
||
//--- центроиды агентов (agentCount обычных + 1 лучший)
|
||
S3DCentroid m_centroids [];
|
||
int m_nCentroids;
|
||
//--- трек лучшего решения (кольцевой буфер ёмкостью m_trailMax точек)
|
||
S3DTrailPt m_trail [];
|
||
int m_trailLen;
|
||
int m_trailHead; // индекс записи в кольцевом буфере
|
||
int m_trailMax;
|
||
public:
|
||
C_TestStand3D();
|
||
~C_TestStand3D()
|
||
{
|
||
Shutdown();
|
||
}
|
||
//--- жизненный цикл
|
||
bool Init(const string name, int x, int y, int w, int h);
|
||
void Shutdown();
|
||
//--- построение поверхности; grid — вершин по одной оси (10..200)
|
||
// вызывать при каждой смене тестовой функции
|
||
bool BuildSurface(C_Function &func, int grid = 80);
|
||
//--- передача позиций агентов
|
||
// args[] = [a0_f0_x, a0_f0_y, a0_f1_x, a0_f1_y, …] — agentCount * funcCount пар
|
||
// bestArgs[] = ao.cB — funcCount пар координат глобально лучшего решения
|
||
void SetAgents(double &args [], int agentCount, int funcCount,
|
||
C_Function &func, double &bestArgs []);
|
||
//--- один кадр рендера (поверхность + трек + агенты)
|
||
void Redraw();
|
||
//--- сброс трека; вызывать в начале каждого нового повторного теста
|
||
void ResetTrail()
|
||
{
|
||
m_trailLen = 0;
|
||
m_trailHead = 0;
|
||
}
|
||
//--- события камеры (маршрутизировать из OnChartEvent в EA-контексте)
|
||
void OnMouseMove(int chart_x, int chart_y, uint flags);
|
||
void OnMouseWheel(double delta);
|
||
void OnDblClick();
|
||
//--- авто-вращение; dt — виртуальный шаг времени в секундах
|
||
// в скрипте передавать фиксированный шаг (например 0.033 = ~30 fps)
|
||
void OnTimer(double dt);
|
||
//--- видимость и размер
|
||
void Show(bool v);
|
||
bool IsVisible() const
|
||
{
|
||
return m_vis;
|
||
}
|
||
string GetName() const
|
||
{
|
||
return m_name;
|
||
}
|
||
void Resize(int new_w, int new_h);
|
||
private:
|
||
//--- пересчёт позиции камеры и VP-матрицы для агентов
|
||
void _CamUpdate();
|
||
//--- проекция мировой точки → экранные пиксели
|
||
// возвращает false если точка вышла за пределы viewport
|
||
bool _Project(float wx, float wy, float wz, int &sx, int &sy);
|
||
//--- маппинг одной координатной пары домена → мировые координаты
|
||
void _MapToWorld(double ax, double ay, C_Function &func,
|
||
double rx, double ry,
|
||
float &wx, float &wy, float &wz);
|
||
//--- отрисовка трека и агентов поверх 3D-сцены
|
||
void _DrawTrail();
|
||
void _DrawAgents();
|
||
//--- портативные матричные хелперы
|
||
static void _LookAtLH(DXMatrix &M,
|
||
DXVector3 &eye, DXVector3 &tgt, DXVector3 &up);
|
||
static void _PerspLH(DXMatrix &M,
|
||
float fov, float aspect, float zn, float zf);
|
||
static void _MulMat(DXMatrix &C, DXMatrix &A, DXMatrix &B);
|
||
//--- радужный цвет: h_deg ∈ [0,360] → ARGB (S=1, L=0.5, полный спектр)
|
||
static uint _HSL(int h_deg);
|
||
};
|
||
//+------------------------------------------------------------------+
|
||
C_TestStand3D::C_TestStand3D()
|
||
: m_surf_ok(false), m_vis(false), m_shutdown_done(false),
|
||
m_nDots(0), m_nCentroids(0), m_grid(80),
|
||
m_pitch(S3D_PITCH_DEF), m_yaw(0.0f), m_dist(S3D_DIST_DEF),
|
||
m_timeout(0.0), m_mx(-1), m_my(-1),
|
||
m_fxMin(0), m_fxMax(1), m_fyMin(0), m_fyMax(1),
|
||
m_trailLen(0), m_trailHead(0), m_trailMax(600)
|
||
{
|
||
ArrayResize(m_trail, m_trailMax);
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
bool C_TestStand3D::Init(const string name, int x, int y, int w, int h)
|
||
{
|
||
m_name = name;
|
||
m_x = x;
|
||
m_y = y;
|
||
m_w = w;
|
||
m_h = h;
|
||
ResetLastError();
|
||
if(!m_cv.CreateBitmapLabel(m_name, m_x, m_y, m_w, m_h,
|
||
COLOR_FORMAT_ARGB_NORMALIZE))
|
||
{
|
||
PrintFormat("C_TestStand3D::Init – ошибка CreateBitmapLabel: %d",
|
||
GetLastError());
|
||
return false;
|
||
}
|
||
m_cv.ProjectionMatrixSet(S3D_FOV, (float)m_w / m_h, 0.1f, 200.0f);
|
||
DXVector3 tgt = DXVector3(0.0f, 0.0f, 0.0f);
|
||
DXVector3 up = DXVector3(0.0f, 1.0f, 0.0f);
|
||
m_cv.ViewTargetSet(tgt);
|
||
m_cv.ViewUpDirectionSet(up);
|
||
//--- освещение: тёплый направленный + холодный ambient
|
||
m_cv.LightColorSet(DXColor(1.0f, 0.97f, 0.88f, 0.32f));
|
||
m_cv.AmbientColorSet(DXColor(0.82f, 0.88f, 1.0f, 0.72f));
|
||
_CamUpdate();
|
||
ObjectSetInteger(0, m_name, OBJPROP_HIDDEN, true);
|
||
ObjectSetInteger(0, m_name, OBJPROP_SELECTABLE, false);
|
||
return true;
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::Shutdown()
|
||
{
|
||
if(m_shutdown_done)
|
||
return;
|
||
m_shutdown_done = true;
|
||
m_surf.Shutdown();
|
||
ObjectDelete(0, m_name);
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
bool C_TestStand3D::BuildSurface(C_Function &func, int grid)
|
||
{
|
||
m_grid = (int)MathMax(10.0, MathMin(200.0, (double)grid));
|
||
m_fxMin = func.GetMinRangeX();
|
||
m_fxMax = func.GetMaxRangeX();
|
||
m_fyMin = func.GetMinRangeY();
|
||
m_fyMax = func.GetMaxRangeY();
|
||
double buf [];
|
||
if(!GenerateDataFixedSize(m_grid, m_grid, func, buf))
|
||
{
|
||
Print("C_TestStand3D::BuildSurface – GenerateDataFixedSize failed");
|
||
return false;
|
||
}
|
||
const float VAL_RANGE = 1.0f;
|
||
DXVector3 bMin = DXVector3(-S3D_SX, -S3D_SY, -S3D_SZ);
|
||
DXVector3 bMax = DXVector3(S3D_SX, S3D_SY, S3D_SZ);
|
||
DXVector2 uv = DXVector2(1.0f, 1.0f);
|
||
if(!m_surf_ok)
|
||
{
|
||
if(!m_surf.Create(m_cv.DXDispatcher(), m_cv.InputScene(),
|
||
buf, (uint)m_grid, (uint)m_grid,
|
||
VAL_RANGE, bMin, bMax, uv,
|
||
CDXSurface::SF_TWO_SIDED | CDXSurface::SF_USE_NORMALS))
|
||
{
|
||
Print("C_TestStand3D::BuildSurface – CDXSurface::Create failed");
|
||
return false;
|
||
}
|
||
m_surf.Update(buf, (uint)m_grid, (uint)m_grid,
|
||
VAL_RANGE, bMin, bMax, uv,
|
||
CDXSurface::SF_TWO_SIDED | CDXSurface::SF_USE_NORMALS,
|
||
CDXSurface::CS_COLD_TO_HOT);
|
||
m_surf.SpecularColorSet(DXColor(1.0f, 1.0f, 1.0f, 0.55f));
|
||
m_cv.ObjectAdd(&m_surf);
|
||
m_surf_ok = true;
|
||
}
|
||
else
|
||
{
|
||
m_surf.Update(buf, (uint)m_grid, (uint)m_grid,
|
||
VAL_RANGE, bMin, bMax, uv,
|
||
CDXSurface::SF_TWO_SIDED | CDXSurface::SF_USE_NORMALS,
|
||
CDXSurface::CS_COLD_TO_HOT);
|
||
}
|
||
m_nDots = 0;
|
||
m_nCentroids = 0;
|
||
m_trailLen = 0;
|
||
m_trailHead = 0;
|
||
return true;
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
//| _MapToWorld — маппинг (ax,ay) из домена функции в мировые коорд. |
|
||
//| rx, ry — предвычисленные (fxMax-fxMin), (fyMax-fyMin) |
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::_MapToWorld(double ax, double ay, C_Function &func,
|
||
double rx, double ry,
|
||
float &wx, float &wy, float &wz)
|
||
{
|
||
ax = MathMax(m_fxMin, MathMin(m_fxMax, ax));
|
||
ay = MathMax(m_fyMin, MathMin(m_fyMax, ay));
|
||
wx = (float)(-S3D_SX + (ax - m_fxMin) / rx * 2.0 * S3D_SX);
|
||
wz = (float)(-S3D_SZ + (ay - m_fyMin) / ry * 2.0 * S3D_SZ);
|
||
double fv = MathMax(0.0, MathMin(1.0, func.Core(ax, ay)));
|
||
wy = (float)(-S3D_SY + fv * 2.0 * S3D_SY) + 0.22f;
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
//| SetAgents |
|
||
//| args[] — agentCount * funcCount пар (x,y), layout: |
|
||
//| [a0_f0_x, a0_f0_y, a0_f1_x, a0_f1_y, …, |
|
||
//| a1_f0_x, a1_f0_y, …] |
|
||
//| bestArgs[] — ao.cB, funcCount пар (x,y) глобально лучшего |
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::SetAgents(double &args [], int agentCount, int funcCount,
|
||
C_Function &func, double &bestArgs [])
|
||
{
|
||
if(agentCount <= 0 || funcCount <= 0)
|
||
return;
|
||
if(ArraySize(args) < agentCount * funcCount * 2)
|
||
return;
|
||
if(ArraySize(bestArgs) < funcCount * 2)
|
||
return;
|
||
|
||
double rx = m_fxMax - m_fxMin;
|
||
if(rx < 1e-9)
|
||
rx = 1e-9;
|
||
double ry = m_fyMax - m_fyMin;
|
||
if(ry < 1e-9)
|
||
ry = 1e-9;
|
||
|
||
//=== 1. Цветные точки проекций (agentCount × funcCount) ===
|
||
// Цвет определяется индексом функции j — как в 2D (DoubleToColor(i, 0, count-1, 0, 270))
|
||
m_nDots = agentCount * funcCount;
|
||
ArrayResize(m_dots, m_nDots);
|
||
|
||
for(int i = 0; i < agentCount; i++)
|
||
for(int j = 0; j < funcCount; j++)
|
||
{
|
||
int idx = i * funcCount + j;
|
||
float wx, wy, wz;
|
||
_MapToWorld(args [idx * 2], args [idx * 2 + 1], func, rx, ry, wx, wy, wz);
|
||
m_dots [idx].wx = wx;
|
||
m_dots [idx].wy = wy;
|
||
m_dots [idx].wz = wz;
|
||
m_dots [idx].clr = _HSL((int)(270.0 * j / MathMax(1, funcCount - 1)));
|
||
}
|
||
|
||
//=== 2. Центроиды агентов (agentCount обычных + 1 лучший) ===
|
||
m_nCentroids = agentCount + 1;
|
||
ArrayResize(m_centroids, m_nCentroids);
|
||
|
||
//--- центроид каждого обычного агента (среднее его funcCount проекций)
|
||
for(int i = 0; i < agentCount; i++)
|
||
{
|
||
double sx = 0.0, sy = 0.0, sz = 0.0;
|
||
for(int j = 0; j < funcCount; j++)
|
||
{
|
||
int idx = i * funcCount + j;
|
||
sx += m_dots [idx].wx;
|
||
sy += m_dots [idx].wy;
|
||
sz += m_dots [idx].wz;
|
||
}
|
||
m_centroids [i].wx = (float)(sx / funcCount);
|
||
m_centroids [i].wy = (float)(sy / funcCount);
|
||
m_centroids [i].wz = (float)(sz / funcCount);
|
||
m_centroids [i].is_best = false;
|
||
}
|
||
|
||
//--- центроид лучшего решения из ao.cB (как st.PointDr(ao.cB, …, true))
|
||
{
|
||
double bsx = 0.0, bsy = 0.0, bsz = 0.0;
|
||
for(int j = 0; j < funcCount; j++)
|
||
{
|
||
float wx, wy, wz;
|
||
_MapToWorld(bestArgs [j * 2], bestArgs [j * 2 + 1], func, rx, ry, wx, wy, wz);
|
||
bsx += wx;
|
||
bsy += wy;
|
||
bsz += wz;
|
||
}
|
||
int bi = agentCount;
|
||
m_centroids [bi].wx = (float)(bsx / funcCount);
|
||
m_centroids [bi].wy = (float)(bsy / funcCount);
|
||
m_centroids [bi].wz = (float)(bsz / funcCount);
|
||
m_centroids [bi].is_best = true;
|
||
|
||
//--- трек отслеживает центроид ao.cB (кольцевой буфер)
|
||
m_trail [m_trailHead].wx = m_centroids [bi].wx;
|
||
m_trail [m_trailHead].wy = m_centroids [bi].wy;
|
||
m_trail [m_trailHead].wz = m_centroids [bi].wz;
|
||
m_trailHead = (m_trailHead + 1) % m_trailMax;
|
||
if(m_trailLen < m_trailMax)
|
||
m_trailLen++;
|
||
}
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::Redraw()
|
||
{
|
||
if(!m_vis || !m_surf_ok)
|
||
return;
|
||
m_cv.Render(DX_CLEAR_COLOR | DX_CLEAR_DEPTH, S3D_BG);
|
||
_DrawTrail();
|
||
_DrawAgents();
|
||
m_cv.Update();
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::_CamUpdate()
|
||
{
|
||
DXVector4 cam = DXVector4(0.0f, 0.0f, -(float)m_dist, 1.0f);
|
||
DXVector4 lgt = DXVector4(0.25f, -0.45f, 1.0f, 0.0f);
|
||
DXMatrix rot;
|
||
DXMatrixRotationX(rot, m_pitch);
|
||
DXVec4Transform(cam, cam, rot);
|
||
DXVec4Transform(lgt, lgt, rot);
|
||
DXMatrixRotationY(rot, m_yaw);
|
||
DXVec4Transform(cam, cam, rot);
|
||
DXVec4Transform(lgt, lgt, rot);
|
||
DXVector3 eye = DXVector3(cam);
|
||
DXVector3 ld = DXVector3(lgt);
|
||
m_cv.ViewPositionSet(eye);
|
||
m_cv.LightDirectionSet(ld);
|
||
DXVector3 tgt = DXVector3(0.0f, 0.0f, 0.0f);
|
||
DXVector3 up = DXVector3(0.0f, 1.0f, 0.0f);
|
||
DXMatrix view, proj;
|
||
_LookAtLH(view, eye, tgt, up);
|
||
_PerspLH(proj, S3D_FOV, (float)m_w / m_h, 0.1f, 200.0f);
|
||
_MulMat(m_vp, view, proj);
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
bool C_TestStand3D::_Project(float wx, float wy, float wz,
|
||
int &sx, int &sy)
|
||
{
|
||
DXVector4 p = DXVector4(wx, wy, wz, 1.0f);
|
||
DXVec4Transform(p, p, m_vp);
|
||
if(p.w < 0.01f)
|
||
return false;
|
||
float nx = p.x / p.w;
|
||
float ny = p.y / p.w;
|
||
if(nx < -1.1f || nx > 1.1f || ny < -1.1f || ny > 1.1f)
|
||
return false;
|
||
sx = (int)((nx + 1.0f) * 0.5f * m_w);
|
||
sy = (int)((1.0f - ny) * 0.5f * m_h);
|
||
return true;
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
//| _DrawTrail |
|
||
//| Золотая линия (#FFC828) по истории позиций лучшего агента. |
|
||
//| Градиент прозрачности: старые точки alpha≈0x30, новые — 0xE0. |
|
||
//| Двойная линия (смещение +1px по Y) — визуальная толщина. |
|
||
//| Обход кольцевого буфера: от oldest к newest по логическому k. |
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::_DrawTrail()
|
||
{
|
||
if(m_trailLen < 2)
|
||
return;
|
||
int px = 0, py = 0, cx = 0, cy = 0;
|
||
bool hasPrev = false;
|
||
//--- индекс самой старой точки в кольцевом буфере
|
||
int start = (m_trailHead - m_trailLen + m_trailMax) % m_trailMax;
|
||
for(int k = 0; k < m_trailLen; k++)
|
||
{
|
||
int t = (start + k) % m_trailMax;
|
||
if(!_Project(m_trail [t].wx, m_trail [t].wy, m_trail [t].wz, cx, cy))
|
||
{
|
||
hasPrev = false;
|
||
continue;
|
||
}
|
||
if(!hasPrev)
|
||
{
|
||
px = cx;
|
||
py = cy;
|
||
hasPrev = true;
|
||
continue;
|
||
}
|
||
double ratio = (double)k / (double)(m_trailLen - 1);
|
||
uint alpha = (uint)(0x30 + ratio * 0xB0);
|
||
uint clr = (alpha << 24) | 0x00FFC828;
|
||
uint clrT = ((alpha >> 1) << 24) | 0x00FFC828;
|
||
m_cv.LineAA(px, py, cx, cy, clr);
|
||
m_cv.LineAA(px, py + 1, cx, cy + 1, clrT);
|
||
px = cx;
|
||
py = cy;
|
||
}
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
//| _DrawAgents |
|
||
//| |
|
||
//| Полное соответствие 2D PointDr: |
|
||
//| — цветные точки r=2 для каждой проекции функции |
|
||
//| (цвет по индексу функции j, hue [0,270]) |
|
||
//| — центроид обычного агента: FillCircle r=4 black + r=3 white |
|
||
//| — центроид лучшего (ao.cB): Circle r=6 + r=7 black (кольцо) |
|
||
//| (радиусы в 3D на 1 больше, чем в 2D, для видимости) |
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::_DrawAgents()
|
||
{
|
||
//--- 1. Цветные точки (аналог FillCircle r=1 в 2D, в 3D r=2 для видимости)
|
||
for(int i = 0; i < m_nDots; i++)
|
||
{
|
||
int sx, sy;
|
||
if(!_Project(m_dots [i].wx, m_dots [i].wy, m_dots [i].wz, sx, sy))
|
||
continue;
|
||
m_cv.FillCircle(sx, sy, 2, m_dots [i].clr);
|
||
}
|
||
|
||
//--- 2. Центроиды агентов + центроид лучшего
|
||
for(int i = 0; i < m_nCentroids; i++)
|
||
{
|
||
int sx, sy;
|
||
if(!_Project(m_centroids [i].wx, m_centroids [i].wy, m_centroids [i].wz, sx, sy))
|
||
continue;
|
||
if(m_centroids [i].is_best)
|
||
{
|
||
//--- аналог 2D: Canvas.Circle(…, 5, clrBlack); Canvas.Circle(…, 6, clrBlack);
|
||
m_cv.Circle(sx, sy, 6, 0xFF000000);
|
||
m_cv.Circle(sx, sy, 7, 0xFF000000);
|
||
}
|
||
else
|
||
{
|
||
//--- аналог 2D: FillCircle(…, 3, clrBlack); FillCircle(…, 2, clrWhite);
|
||
m_cv.FillCircle(sx, sy, 4, 0xFF000000);
|
||
m_cv.FillCircle(sx, sy, 3, 0xFFFFFFFF);
|
||
}
|
||
}
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::OnMouseMove(int chart_x, int chart_y, uint flags)
|
||
{
|
||
int lx = chart_x - m_x;
|
||
int ly = chart_y - m_y;
|
||
if((flags & 1) == 1)
|
||
{
|
||
if(m_mx >= 0)
|
||
{
|
||
m_yaw += (lx - m_mx) / 240.0f;
|
||
m_pitch += (ly - m_my) / 240.0f;
|
||
float pMax = (float)(DX_PI * 0.48);
|
||
if(m_pitch < -pMax)
|
||
m_pitch = -pMax;
|
||
if(m_pitch > pMax)
|
||
m_pitch = pMax;
|
||
_CamUpdate();
|
||
}
|
||
m_mx = lx;
|
||
m_my = ly;
|
||
m_timeout = S3D_TIMEOUT;
|
||
}
|
||
else
|
||
{
|
||
m_mx = -1;
|
||
m_my = -1;
|
||
}
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::OnMouseWheel(double delta)
|
||
{
|
||
m_dist *= (1.0 - delta * 0.0012);
|
||
m_dist = MathMax(S3D_DIST_MIN, MathMin(S3D_DIST_MAX, m_dist));
|
||
_CamUpdate();
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::OnDblClick()
|
||
{
|
||
m_pitch = S3D_PITCH_DEF;
|
||
m_yaw = 0.0f;
|
||
m_dist = S3D_DIST_DEF;
|
||
m_timeout = 0.0;
|
||
_CamUpdate();
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::OnTimer(double dt)
|
||
{
|
||
m_timeout -= dt;
|
||
if(m_timeout < 0.0)
|
||
{
|
||
m_yaw += (float)(dt * S3D_AUTO_YAW);
|
||
m_pitch = (float)((1.0 - S3D_RETURN) * m_pitch + S3D_RETURN * S3D_PITCH_DEF);
|
||
_CamUpdate();
|
||
}
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::Show(bool v)
|
||
{
|
||
m_vis = v;
|
||
ObjectSetInteger(0, m_name, OBJPROP_HIDDEN, !v);
|
||
ChartRedraw();
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::Resize(int new_w, int new_h)
|
||
{
|
||
if(new_w == m_w && new_h == m_h)
|
||
return;
|
||
m_w = new_w;
|
||
m_h = new_h;
|
||
m_cv.Resize(m_w, m_h);
|
||
DXContextSetSize(m_cv.DXContext(), m_w, m_h);
|
||
m_cv.ProjectionMatrixSet(S3D_FOV, (float)m_w / m_h, 0.1f, 200.0f);
|
||
_CamUpdate();
|
||
}
|
||
//════════════════════════════════════════════════════════════════════
|
||
// Матричные хелперы
|
||
//════════════════════════════════════════════════════════════════════
|
||
void C_TestStand3D::_LookAtLH(DXMatrix &M,
|
||
DXVector3 &eye, DXVector3 &tgt, DXVector3 &up)
|
||
{
|
||
float zx = tgt.x - eye.x, zy = tgt.y - eye.y, zz = tgt.z - eye.z;
|
||
float zl = (float)MathSqrt(zx * zx + zy * zy + zz * zz);
|
||
if(zl < 1e-7f)
|
||
zl = 1e-7f;
|
||
zx /= zl;
|
||
zy /= zl;
|
||
zz /= zl;
|
||
float xx = up.y * zz - up.z * zy;
|
||
float xy = up.z * zx - up.x * zz;
|
||
float xz = up.x * zy - up.y * zx;
|
||
float xl = (float)MathSqrt(xx * xx + xy * xy + xz * xz);
|
||
if(xl < 1e-7f)
|
||
xl = 1e-7f;
|
||
xx /= xl;
|
||
xy /= xl;
|
||
xz /= xl;
|
||
float yx = zy * xz - zz * xy;
|
||
float yy = zz * xx - zx * xz;
|
||
float yz = zx * xy - zy * xx;
|
||
float dx = -(xx * eye.x + xy * eye.y + xz * eye.z);
|
||
float dy = -(yx * eye.x + yy * eye.y + yz * eye.z);
|
||
float dz = -(zx * eye.x + zy * eye.y + zz * eye.z);
|
||
M.m [0] [0] = xx;
|
||
M.m [0] [1] = yx;
|
||
M.m [0] [2] = zx;
|
||
M.m [0] [3] = 0.0f;
|
||
M.m [1] [0] = xy;
|
||
M.m [1] [1] = yy;
|
||
M.m [1] [2] = zy;
|
||
M.m [1] [3] = 0.0f;
|
||
M.m [2] [0] = xz;
|
||
M.m [2] [1] = yz;
|
||
M.m [2] [2] = zz;
|
||
M.m [2] [3] = 0.0f;
|
||
M.m [3] [0] = dx;
|
||
M.m [3] [1] = dy;
|
||
M.m [3] [2] = dz;
|
||
M.m [3] [3] = 1.0f;
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::_PerspLH(DXMatrix &M,
|
||
float fov, float aspect, float zn, float zf)
|
||
{
|
||
float ys = 1.0f / (float)MathTan(fov * 0.5);
|
||
float xs = ys / aspect;
|
||
float zr = zf / (zf - zn);
|
||
M.m [0] [0] = xs;
|
||
M.m [0] [1] = 0.0f;
|
||
M.m [0] [2] = 0.0f;
|
||
M.m [0] [3] = 0.0f;
|
||
M.m [1] [0] = 0.0f;
|
||
M.m [1] [1] = ys;
|
||
M.m [1] [2] = 0.0f;
|
||
M.m [1] [3] = 0.0f;
|
||
M.m [2] [0] = 0.0f;
|
||
M.m [2] [1] = 0.0f;
|
||
M.m [2] [2] = zr;
|
||
M.m [2] [3] = 1.0f;
|
||
M.m [3] [0] = 0.0f;
|
||
M.m [3] [1] = 0.0f;
|
||
M.m [3] [2] = -zn * zr;
|
||
M.m [3] [3] = 0.0f;
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
void C_TestStand3D::_MulMat(DXMatrix &C, DXMatrix &A, DXMatrix &B)
|
||
{
|
||
C.m [0] [0] = A.m [0] [0] * B.m [0] [0] + A.m [0] [1] * B.m [1] [0] + A.m [0] [2] * B.m [2] [0] + A.m [0] [3] * B.m [3] [0];
|
||
C.m [0] [1] = A.m [0] [0] * B.m [0] [1] + A.m [0] [1] * B.m [1] [1] + A.m [0] [2] * B.m [2] [1] + A.m [0] [3] * B.m [3] [1];
|
||
C.m [0] [2] = A.m [0] [0] * B.m [0] [2] + A.m [0] [1] * B.m [1] [2] + A.m [0] [2] * B.m [2] [2] + A.m [0] [3] * B.m [3] [2];
|
||
C.m [0] [3] = A.m [0] [0] * B.m [0] [3] + A.m [0] [1] * B.m [1] [3] + A.m [0] [2] * B.m [2] [3] + A.m [0] [3] * B.m [3] [3];
|
||
C.m [1] [0] = A.m [1] [0] * B.m [0] [0] + A.m [1] [1] * B.m [1] [0] + A.m [1] [2] * B.m [2] [0] + A.m [1] [3] * B.m [3] [0];
|
||
C.m [1] [1] = A.m [1] [0] * B.m [0] [1] + A.m [1] [1] * B.m [1] [1] + A.m [1] [2] * B.m [2] [1] + A.m [1] [3] * B.m [3] [1];
|
||
C.m [1] [2] = A.m [1] [0] * B.m [0] [2] + A.m [1] [1] * B.m [1] [2] + A.m [1] [2] * B.m [2] [2] + A.m [1] [3] * B.m [3] [2];
|
||
C.m [1] [3] = A.m [1] [0] * B.m [0] [3] + A.m [1] [1] * B.m [1] [3] + A.m [1] [2] * B.m [2] [3] + A.m [1] [3] * B.m [3] [3];
|
||
C.m [2] [0] = A.m [2] [0] * B.m [0] [0] + A.m [2] [1] * B.m [1] [0] + A.m [2] [2] * B.m [2] [0] + A.m [2] [3] * B.m [3] [0];
|
||
C.m [2] [1] = A.m [2] [0] * B.m [0] [1] + A.m [2] [1] * B.m [1] [1] + A.m [2] [2] * B.m [2] [1] + A.m [2] [3] * B.m [3] [1];
|
||
C.m [2] [2] = A.m [2] [0] * B.m [0] [2] + A.m [2] [1] * B.m [1] [2] + A.m [2] [2] * B.m [2] [2] + A.m [2] [3] * B.m [3] [2];
|
||
C.m [2] [3] = A.m [2] [0] * B.m [0] [3] + A.m [2] [1] * B.m [1] [3] + A.m [2] [2] * B.m [2] [3] + A.m [2] [3] * B.m [3] [3];
|
||
C.m [3] [0] = A.m [3] [0] * B.m [0] [0] + A.m [3] [1] * B.m [1] [0] + A.m [3] [2] * B.m [2] [0] + A.m [3] [3] * B.m [3] [0];
|
||
C.m [3] [1] = A.m [3] [0] * B.m [0] [1] + A.m [3] [1] * B.m [1] [1] + A.m [3] [2] * B.m [2] [1] + A.m [3] [3] * B.m [3] [1];
|
||
C.m [3] [2] = A.m [3] [0] * B.m [0] [2] + A.m [3] [1] * B.m [1] [2] + A.m [3] [2] * B.m [2] [2] + A.m [3] [3] * B.m [3] [2];
|
||
C.m [3] [3] = A.m [3] [0] * B.m [0] [3] + A.m [3] [1] * B.m [1] [3] + A.m [3] [2] * B.m [2] [3] + A.m [3] [3] * B.m [3] [3];
|
||
}
|
||
//+------------------------------------------------------------------+
|
||
uint C_TestStand3D::_HSL(int h_deg)
|
||
{
|
||
double hue = h_deg / 360.0;
|
||
double off [3] = {1.0 / 3.0, 0.0, -1.0 / 3.0};
|
||
uint ch [3];
|
||
for(int i = 0; i < 3; i++)
|
||
{
|
||
double vh = hue + off [i];
|
||
if(vh < 0.0)
|
||
vh += 1.0;
|
||
if(vh > 1.0)
|
||
vh -= 1.0;
|
||
double v;
|
||
if(6.0 * vh < 1.0)
|
||
v = 6.0 * vh;
|
||
else
|
||
if(2.0 * vh < 1.0)
|
||
v = 1.0;
|
||
else
|
||
if(3.0 * vh < 2.0)
|
||
v = (2.0 / 3.0 - vh) * 6.0;
|
||
else
|
||
v = 0.0;
|
||
ch [i] = (uint)(v * 255.0);
|
||
}
|
||
return 0xFF000000 | (ch [0] << 16) | (ch [1] << 8) | ch [2];
|
||
}
|
||
//+------------------------------------------------------------------+
|