Files
2026-04-28 00:29:35 +04:00

688 lines
53 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| 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];
}
//+------------------------------------------------------------------+