mql-for-begginers/Experts/Examples/Math 3D Morpher/Math 3D Morpher.mq5

455 lines
35 KiB
MQL5
Raw Permalink Normal View History

2025-07-22 18:30:17 +03:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| DemoMorphMath3D.mq5 |
//| Copyright 2000-2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//---
#include <Controls\Picture.mqh>
#include <Canvas\Canvas3D.mqh>
#include <Canvas\DX\DXSurface.mqh>
#include "Functions.mqh"
//--- resources
#define TEXTURE_CHECKER "::Textures/checker.bmp"
#resource "Textures/checker.bmp"
//-- input data
input double Inp_Morph_Time =3.0; // Morphing time
input double Inp_Freeze_Time=1.0; // Fixing on result time
input int Inp_Grid_Size =150; // Grid size
input color Inp_Background =clrWhite;// Background color
input bool Inp_UseChess =false; // Use chess texture
//+---------------------------------------------------------------------+
//| Chart constants |
//+---------------------------------------------------------------------+
#define CAMERA_ANGLES_TIMEOUT 3.0f
#define CAMERA_DISTANCE_TIMEOUT 3.0f
#define CAMERA_DISTANCE 40.0f
#define CAMERA_RETURN_STRENGTH 0.01f
#define CAMERA_ANGLE_Y_DEFAULT DX_PI/6.0f
#define CAMERA_ANGLE_Y_SPEED 0.3f
//+------------------------------------------------------------------+
//| GenerateDataFixedSize |
//+------------------------------------------------------------------+
bool NormalizeData(double &data[],double &data_min,double &data_max)
{
int data_size=ArraySize(data);
if(data_size<1)
return(false);
data_min=DBL_MAX;
data_max=-DBL_MAX;
for(int i=0; i<data_size; i++)
{
if(data_min>data[i])
data_min=data[i];
if(data_max<data[i])
data_max=data[i];
}
if(data_max-data_min<0.00001)
{
data_min=(data_max+data_min)/2.0-0.000005;
data_max=data_min+0.00001;
}
double scale=1.0/(data_max-data_min);
for(int i=0; i<data_size; i++)
{
data[i]*=scale;
}
data_min*=scale;
data_max*=scale;
return(true);
}
//+------------------------------------------------------------------+
//| Application window |
//+------------------------------------------------------------------+
class CCanvas3DWindow
{
protected:
CPicture m_picture;
CCanvas3D m_canvas;
//--- canvas data
int m_width;
int m_height;
uint m_background_color;
uint m_text_color;
//--- source functions data
int morph_id1;
int morph_id2;
int m_data_size;
double m_data1[];
double m_data2[];
double m_data1_min;
double m_data2_min;
double m_data1_max;
double m_data2_max;
int m_functions_count;
//--- last frame time
double m_last_frame;
//--- morph data
double m_current_morph_factor;
double m_morpth_time;
double m_freeze_time;
//--- camera data
DXVector2 m_camera_angles;
double m_camera_angles_timeout;
double m_camera_distance;
double m_camera_distance_timeout;
//--- input data
int m_mouse_x_old;
int m_mouse_y_old;
//---
CDXSurface m_surface;
public:
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CCanvas3DWindow(void):m_mouse_x_old(-1),m_mouse_y_old(-1)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
~CCanvas3DWindow(void)
{
//--- shutdown mesh
m_surface.Shutdown();
}
//+------------------------------------------------------------------+
//| DrawMesh |
//+------------------------------------------------------------------+
bool UpdateMesh(void)
{
//--- calculate blend value
double blend=sin(DX_PI*m_current_morph_factor/m_morpth_time-DX_PI/2.0)*0.5+0.5;
//--- blend data
double data[];
int data_size=m_data_size*m_data_size;
if(ArraySize(m_data1)<data_size || ArraySize(m_data2)<data_size || ArrayResize(data,data_size)<data_size)
return(false);
float min_value=(float)((1.0-blend)*m_data1_min+blend*m_data2_min);
float max_value=(float)((1.0-blend)*m_data1_max+blend*m_data2_max);
for(int j=0; j<m_data_size; j++)
for(int i=0; i<m_data_size; i++)
{
int idx=j*m_data_size+i;
data[idx]=(1.0-blend)*m_data1[idx]+blend*m_data2[idx];
}
//--- create new object
m_surface.Update(data,(uint)m_data_size,(uint)m_data_size,max_value-min_value,DXVector3(-10,-5,-10),DXVector3(10,5,10),DXVector2(1.0f,1.0f),CDXSurface::SF_TWO_SIDED|CDXSurface::SF_USE_NORMALS,CDXSurface::CS_COLD_TO_HOT);
//---
return(true);
}
//+------------------------------------------------------------------+
//| Update camera position |
//+------------------------------------------------------------------+
bool UpdateCamera(void)
{
DXVector4 camera=DXVector4(0.0f,0.0f,-(float)m_camera_distance,1.0f);
DXVector4 light =DXVector4(0.25f,-0.25f,1.0f,0.0f);
DXMatrix rotation;
DXMatrixRotationX(rotation,m_camera_angles.x);
DXVec4Transform(camera,camera,rotation);
DXVec4Transform(light, light, rotation);
DXMatrixRotationY(rotation,m_camera_angles.y);
DXVec4Transform(camera,camera,rotation);
DXVec4Transform(light, light, rotation);
m_canvas.ViewPositionSet(DXVector3(camera));
m_canvas.LightDirectionSet(DXVector3(light));
//---
return(true);
}
//+------------------------------------------------------------------+
//| Create |
//+------------------------------------------------------------------+
virtual bool Create(const int width,const int height)
{
//--- prepare the chart
ChartSetInteger(0,CHART_SHOW,false);
ChartRedraw();
//--- save sizes
m_width=width;
m_height=height;
//---
ResetLastError();
if(!m_canvas.CreateBitmapLabel("Morphing",0,0,m_width,m_height,COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("Error creating canvas: ",GetLastError());
return(false);
}
//--- set colors
m_background_color=ColorToARGB(Inp_Background);
if((GETRGBR(m_background_color)+GETRGBG(m_background_color)+GETRGBB(m_background_color))/3<128)
m_text_color=ColorToARGB(clrWhite);
else
m_text_color=ColorToARGB(clrNavy);
//---
m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)m_width/m_height,0.1f,100.0f);
m_canvas.ViewTargetSet(DXVector3(0.0,-2.0,0.0));
m_canvas.ViewUpDirectionSet(DXVector3(0.0,1.0,0.0));
m_canvas.LightColorSet(DXColor(1.0f,1.0f,0.9f,0.27f));
m_canvas.AmbientColorSet(DXColor(0.9f,0.9f,1.0f,0.8f));
//--- set camera parameters
m_camera_distance=CAMERA_DISTANCE;
m_camera_distance_timeout=0.0;
m_camera_angles=DXVector2(CAMERA_ANGLE_Y_DEFAULT,0.0);
m_camera_angles_timeout=0.0;
//--- set timers
m_morpth_time=fabs(Inp_Morph_Time);
m_freeze_time=fabs(Inp_Freeze_Time);
//--- initial and final morph function
m_functions_count=ArraySize(ExtFunctionsNames);
morph_id1=MathRand()%m_functions_count;
morph_id2=MathRand()%m_functions_count;
if(morph_id2==morph_id1)
{
morph_id2=(morph_id1+1)%m_functions_count;
}
//--- initial morph settings
m_current_morph_factor=0.0;
m_last_frame=GetMicrosecondCount()/1000000.0;
//--- save grid size with up and top limits
m_data_size=Inp_Grid_Size;
if(m_data_size<5)
m_data_size=5;
if(m_data_size>500)
m_data_size=500;
//--- generate end states data
GenerateDataFixedSize(m_data_size,m_data_size,(EnMathFunction)morph_id1,m_data1);
NormalizeData(m_data1,m_data1_min,m_data1_max);
GenerateDataFixedSize(m_data_size,m_data_size,(EnMathFunction)morph_id2,m_data2);
NormalizeData(m_data2,m_data2_min,m_data2_max);
//--- create mesh
if(!m_surface.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),m_data1,(uint)m_data_size,(uint)m_data_size,float(m_data1_max-m_data1_min),DXVector3(-10,-5,-10),DXVector3(10,5,10),DXVector2(1.0f,1.0f),CDXSurface::SF_TWO_SIDED|CDXSurface::SF_USE_NORMALS))
{
m_canvas.Destroy();
return(false);
}
//--- set mesh parameters
m_surface.SpecularColorSet(DXColor(1.0f,1.0f,1.0f,1.0f));
if(Inp_UseChess)
m_surface.TextureSet(m_canvas.DXDispatcher(),TEXTURE_CHECKER);
//--- add mesh to scene
m_canvas.ObjectAdd(&m_surface);
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Update frame |
//+------------------------------------------------------------------+
void Redraw()
{
//--- render 3D
m_canvas.Render(DX_CLEAR_COLOR|DX_CLEAR_DEPTH,m_background_color);
//--- draw text label
int left=25,top=15;
static int pos=left;
m_canvas.FontSet("Arial",64,FW_BLACK);
//---
if(m_current_morph_factor<m_morpth_time)
{
//--- first function
m_canvas.TextOut(left,top,ExtFunctionsNames[morph_id1],m_text_color,0);
left+=m_canvas.TextWidth(ExtFunctionsNames[morph_id1]);
//--- arrow
m_canvas.TextOut(left,top-3," \x2192 ",m_text_color,0);
left+=m_canvas.TextWidth(" \x2192 ");
pos=left;
}
else
{
//--- move second function label to left while morphing freezed
double t=(m_current_morph_factor-m_morpth_time)/m_freeze_time;
t=0.5+0.5*sin(M_PI*t-M_PI/2.0);
left=(int)((1-t)*pos+t*left);
}
//--- second function
m_canvas.TextOut(left,top,ExtFunctionsNames[morph_id2],m_text_color,0);
//--- update chart
m_canvas.Update();
}
//+------------------------------------------------------------------+
//| Process mouse moving event |
//+------------------------------------------------------------------+
void OnMouseMove(int x,int y,uint flags)
{
if((flags&1)==1)
{
if(m_mouse_x_old!=-1)
{
m_camera_angles.y+=(x-m_mouse_x_old)/300.0f;
m_camera_angles.x+=(y-m_mouse_y_old)/300.0f;
if(m_camera_angles.x<-DX_PI*0.49f)
m_camera_angles.x=-DX_PI*0.49f;
if(m_camera_angles.x>DX_PI*0.49f)
m_camera_angles.x=DX_PI*0.49f;
//---
UpdateCamera();
}
//---
m_mouse_x_old=x;
m_mouse_y_old=y;
//---
m_camera_angles_timeout=CAMERA_ANGLES_TIMEOUT;
}
else
{
m_mouse_x_old=-1;
m_mouse_y_old=-1;
}
}
//+------------------------------------------------------------------+
//| Process mouse moving event |
//+------------------------------------------------------------------+
void OnMouseWheel(double delta)
{
m_camera_distance*=1.0-delta*0.001;
if(m_camera_distance>75.0)
m_camera_distance=75.0;
if(m_camera_distance<20.0)
m_camera_distance=20.0;
UpdateCamera();
m_camera_distance_timeout=CAMERA_DISTANCE_TIMEOUT;
}
//+------------------------------------------------------------------+
//| Process chart change event |
//+------------------------------------------------------------------+
void OnChartChange(void)
{
//--- get current chart window size
int w=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
int h=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
//--- update size everywhere it needed
if(w!=m_width || h!=m_height)
{
m_width =w;
m_height=h;
m_canvas.Resize(w,h);
DXContextSetSize(m_canvas.DXContext(),w,h);
m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)m_width/m_height,0.1f,100.0f);
Redraw();
}
}
//+------------------------------------------------------------------+
//| Timer handler |
//+------------------------------------------------------------------+
void OnTimer(void)
{
double current_frame=GetMicrosecondCount()/1000000.0;
double deltatime=current_frame-m_last_frame;
if(deltatime>0.1)
deltatime=0.1;
m_last_frame=current_frame;
m_current_morph_factor+=deltatime;
if(m_current_morph_factor>=m_morpth_time+m_freeze_time)
{
m_current_morph_factor=0.0;
int prev_id=morph_id1;
//--- save second data to first
morph_id1=morph_id2;
ArraySwap(m_data1,m_data2);
m_data1_min=m_data2_min;
m_data1_max=m_data2_max;
//--- generate new second data
while(morph_id2==morph_id1 || morph_id2==prev_id)
{
morph_id2=MathRand()%m_functions_count;
}
GenerateDataFixedSize(m_data_size,m_data_size,(EnMathFunction)morph_id2,m_data2);
NormalizeData(m_data2,m_data2_min,m_data2_max);
}
//--- generate morphed mesh
if(m_current_morph_factor<=m_morpth_time)
UpdateMesh();
//---
m_camera_angles_timeout-=deltatime;
if(m_camera_angles_timeout<0.0)
{
m_camera_angles.y+=(float)deltatime*CAMERA_ANGLE_Y_SPEED;
m_camera_angles.x=(1.0f-CAMERA_RETURN_STRENGTH)*m_camera_angles.x+CAMERA_RETURN_STRENGTH*CAMERA_ANGLE_Y_DEFAULT;
UpdateCamera();
}
m_camera_distance_timeout-=deltatime;
if(m_camera_distance_timeout<0.0 && m_camera_angles_timeout<0.0)
{
m_camera_distance=(1.0f-CAMERA_RETURN_STRENGTH)*m_camera_distance+CAMERA_RETURN_STRENGTH*CAMERA_DISTANCE;
UpdateCamera();
}
//---
Redraw();
}
};
//--- Global window
CCanvas3DWindow *ExtAppWindow=NULL;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,1);
ChartSetInteger(0,CHART_EVENT_MOUSE_WHEEL,1);
//--- get current chart window size
int width =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
//--- create canvas
ExtAppWindow=new CCanvas3DWindow();
if(!ExtAppWindow.Create(width,height))
return(INIT_FAILED);
//--- set timer
EventSetMillisecondTimer(10);
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- destroy
delete ExtAppWindow;
//--- kill timer
EventKillTimer();
//--- revert chart showing mode
ChartSetInteger(0,CHART_SHOW,true);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
ExtAppWindow.OnTimer();
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//---
if(id==CHARTEVENT_KEYDOWN)
{
if(lparam==27)
ExpertRemove();
}
if(id==CHARTEVENT_CHART_CHANGE)
ExtAppWindow.OnChartChange();
//--- process mouse moving
if(id==CHARTEVENT_MOUSE_MOVE)
ExtAppWindow.OnMouseMove((int)lparam,(int)dparam,(uint)sparam);
if(id==CHARTEVENT_MOUSE_WHEEL)
ExtAppWindow.OnMouseWheel(dparam);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+