forked from rosh/MQL5Book
437 lines
12 KiB
MQL5
437 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| ObjectShapesDraw.mq5 |
| |||
//| Copyright 2021, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#include "..\..\Include\AutoPtr.mqh"
| |||
#include "..\..\Include\TypeName.mqh"
| |||
| |||
#define FIGURES 21
| |||
| |||
class Shape;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Base shape class for drawing |
| |||
//+------------------------------------------------------------------+
| |||
class Shape
| |||
{
| |||
public:
| |||
class Registrator
| |||
{
| |||
static Registrator *regs[];
| |||
int shapeCount;
| |||
public:
| |||
const string type;
| |||
Registrator(const string t) : type(t), shapeCount(0)
| |||
{
| |||
const int n = ArraySize(regs);
| |||
ArrayResize(regs, n + 1);
| |||
regs[n] = &this;
| |||
}
| |||
| |||
static int getTypeCount()
| |||
{
| |||
return ArraySize(regs);
| |||
}
| |||
| |||
static Registrator *get(const int i)
| |||
{
| |||
return regs[i];
| |||
}
| |||
| |||
int increment()
| |||
{
| |||
return ++shapeCount;
| |||
}
| |||
| |||
int getShapeCount() const
| |||
{
| |||
return shapeCount;
| |||
}
| |||
| |||
virtual Shape *create(const int px, const int py, const color back,
| |||
const int ¶meters[]) = 0;
| |||
};
| |||
| |||
protected:
| |||
int x, y;
| |||
color backgroundColor;
| |||
const string type;
| |||
string name;
| |||
| |||
Shape(int px, int py, color back, string t) :
| |||
x(px), y(py),
| |||
backgroundColor(back),
| |||
type(t)
| |||
{
| |||
}
| |||
| |||
public:
| |||
~Shape()
| |||
{
| |||
ObjectDelete(0, name);
| |||
}
| |||
| |||
virtual void draw() = 0;
| |||
virtual void setup(const int ¶meters[]) = 0;
| |||
// TODO: read from and write to a file
| |||
//virtual void load(const int handle) = 0;
| |||
//virtual void save(const int handle) = 0;
| |||
| |||
Shape *setColor(const color c)
| |||
{
| |||
backgroundColor = c;
| |||
return &this;
| |||
}
| |||
| |||
Shape *moveX(const int _x)
| |||
{
| |||
x += _x;
| |||
return &this;
| |||
}
| |||
| |||
Shape *moveY(const int _y)
| |||
{
| |||
y += _y;
| |||
return &this;
| |||
}
| |||
| |||
Shape *move(const int _x, const int _y)
| |||
{
| |||
x += _x;
| |||
y += _y;
| |||
return &this;
| |||
}
| |||
| |||
string toString() const
| |||
{
| |||
return type + " " + (string)x + " " + (string)y;
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Templatized helper class for Shape-derived classes registration |
| |||
//+------------------------------------------------------------------+
| |||
template<typename T>
| |||
class MyRegistrator : public Shape::Registrator
| |||
{
| |||
public:
| |||
MyRegistrator() : Registrator(TYPENAME(T))
| |||
{
| |||
}
| |||
| |||
virtual Shape *create(const int px, const int py, const color back,
| |||
const int ¶meters[]) override
| |||
{
| |||
T *temp = new T(px, py, back);
| |||
temp.setup(parameters);
| |||
return temp;
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Rectangle shape |
| |||
//+------------------------------------------------------------------+
| |||
class Rectangle : public Shape
| |||
{
| |||
static MyRegistrator<Rectangle> r;
| |||
| |||
protected:
| |||
int dx, dy; // size (width, height)
| |||
| |||
Rectangle(int px, int py, color back, string t) :
| |||
Shape(px, py, back, t), dx(1), dy(1)
| |||
{
| |||
}
| |||
| |||
public:
| |||
Rectangle(int px, int py, color back) :
| |||
Shape(px, py, back, TYPENAME(this)), dx(1), dy(1)
| |||
{
| |||
name = TYPENAME(this) + (string)r.increment();
| |||
}
| |||
| |||
virtual void setup(const int ¶meters[]) override
| |||
{
| |||
if(ArraySize(parameters) < 2)
| |||
{
| |||
Print("Insufficient parameters for Rectangle");
| |||
return;
| |||
}
| |||
dx = parameters[0];
| |||
dy = parameters[1];
| |||
}
| |||
| |||
void draw() override
| |||
{
| |||
// Print("Drawing rectangle");
| |||
int subw;
| |||
datetime t;
| |||
double p;
| |||
ChartXYToTimePrice(0, x, y, subw, t, p);
| |||
ObjectCreate(0, name, OBJ_RECTANGLE, 0, t, p);
| |||
ChartXYToTimePrice(0, x + dx, y + dy, subw, t, p);
| |||
ObjectSetInteger(0, name, OBJPROP_TIME, 1, t);
| |||
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, p);
| |||
| |||
ObjectSetInteger(0, name, OBJPROP_COLOR, backgroundColor);
| |||
ObjectSetInteger(0, name, OBJPROP_FILL, true);
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Square shape |
| |||
//+------------------------------------------------------------------+
| |||
class Square : public Rectangle
| |||
{
| |||
static MyRegistrator<Square> r;
| |||
public:
| |||
Square(int px, int py, color back) :
| |||
Rectangle(px, py, back, TYPENAME(this))
| |||
{
| |||
name = TYPENAME(this) + (string)r.increment();
| |||
}
| |||
| |||
virtual void setup(const int ¶meters[]) override
| |||
{
| |||
if(ArraySize(parameters) < 1)
| |||
{
| |||
Print("Insufficient parameters for Square");
| |||
return;
| |||
}
| |||
dx = dy = parameters[0];
| |||
}
| |||
| |||
void draw() override
| |||
{
| |||
// Print("Drawing square (delegated to parent)");
| |||
Rectangle::draw();
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Ellipse shape |
| |||
//+------------------------------------------------------------------+
| |||
class Ellipse : public Shape
| |||
{
| |||
static MyRegistrator<Ellipse> r;
| |||
protected:
| |||
int dx, dy; // large and small radiuses
| |||
| |||
Ellipse(int px, int py, color back, string t) :
| |||
Shape(px, py, back, t), dx(1), dy(1)
| |||
{
| |||
}
| |||
| |||
public:
| |||
Ellipse(int px, int py, color back) :
| |||
Shape(px, py, back, TYPENAME(this)), dx(1), dy(1)
| |||
{
| |||
name = TYPENAME(this) + (string)r.increment();
| |||
}
| |||
| |||
virtual void setup(const int ¶meters[]) override
| |||
{
| |||
if(ArraySize(parameters) < 2)
| |||
{
| |||
Print("Insufficient parameters for Ellipse");
| |||
return;
| |||
}
| |||
dx = parameters[0]; // first radius
| |||
dy = parameters[1]; // second radius
| |||
}
| |||
| |||
void draw() override
| |||
{
| |||
// Print("Drawing ellipse");
| |||
int subw;
| |||
datetime t;
| |||
double p;
| |||
| |||
// (x, y) is a center
| |||
// p0: x + dx, y
| |||
// p1: x - dx, y
| |||
// p2: x, y + dy
| |||
| |||
ChartXYToTimePrice(0, x + dx, y, subw, t, p);
| |||
ObjectCreate(0, name, OBJ_ELLIPSE, 0, t, p);
| |||
ChartXYToTimePrice(0, x - dx, y, subw, t, p);
| |||
ObjectSetInteger(0, name, OBJPROP_TIME, 1, t);
| |||
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, p);
| |||
ChartXYToTimePrice(0, x, y + dy, subw, t, p);
| |||
ObjectSetInteger(0, name, OBJPROP_TIME, 2, t);
| |||
ObjectSetDouble(0, name, OBJPROP_PRICE, 2, p);
| |||
| |||
ObjectSetInteger(0, name, OBJPROP_COLOR, backgroundColor);
| |||
ObjectSetInteger(0, name, OBJPROP_FILL, true);
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Circle shape |
| |||
//+------------------------------------------------------------------+
| |||
class Circle : public Ellipse
| |||
{
| |||
static MyRegistrator<Circle> r;
| |||
public:
| |||
Circle(int px, int py, color back) :
| |||
Ellipse(px, py, back, TYPENAME(this))
| |||
{
| |||
name = TYPENAME(this) + (string)r.increment();
| |||
}
| |||
| |||
virtual void setup(const int ¶meters[]) override
| |||
{
| |||
if(ArraySize(parameters) < 1)
| |||
{
| |||
Print("Insufficient parameters for Circle");
| |||
return;
| |||
}
| |||
dx = dy = parameters[0];
| |||
}
| |||
| |||
void draw() override
| |||
{
| |||
// Print("Drawing circle (delegated to parent)");
| |||
Ellipse::draw();
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Triangle shape |
| |||
//+------------------------------------------------------------------+
| |||
class Triangle: public Shape
| |||
{
| |||
static MyRegistrator<Triangle> r;
| |||
protected:
| |||
int dx; // single side (equilateral)
| |||
public:
| |||
Triangle(int px, int py, color back) :
| |||
Shape(px, py, back, TYPENAME(this)), dx(1)
| |||
{
| |||
name = TYPENAME(this) + (string)r.increment();
| |||
}
| |||
| |||
virtual void setup(const int ¶meters[]) override
| |||
{
| |||
if(ArraySize(parameters) < 1)
| |||
{
| |||
Print("Insufficient parameters for Triangle");
| |||
return;
| |||
}
| |||
dx = parameters[0];
| |||
}
| |||
| |||
void draw()
| |||
{
| |||
// Print("Drawing triangle");
| |||
int subw;
| |||
datetime t;
| |||
double p;
| |||
| |||
const double R = dx * sqrt(3) / 3;
| |||
// (x, y) is a center
| |||
// R = a * sqrt(3) / 3
| |||
// p0: x, y + R
| |||
// p1: x - R * cos(30), y - R * sin(30)
| |||
// p2: x + R * cos(30), y - R * sin(30)
| |||
| |||
ChartXYToTimePrice(0, x, y + (int)R, subw, t, p);
| |||
ObjectCreate(0, name, OBJ_TRIANGLE, 0, t, p);
| |||
ChartXYToTimePrice(0, x - (int)(R * cos(30 * M_PI / 180)),
| |||
y - (int)(R * sin(30 * M_PI / 180)), subw, t, p);
| |||
ObjectSetInteger(0, name, OBJPROP_TIME, 1, t);
| |||
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, p);
| |||
ChartXYToTimePrice(0, x + (int)(R * cos(30 * M_PI / 180)),
| |||
y - (int)(R * sin(30 * M_PI / 180)), subw, t, p);
| |||
ObjectSetInteger(0, name, OBJPROP_TIME, 2, t);
| |||
ObjectSetDouble(0, name, OBJPROP_PRICE, 2, p);
| |||
| |||
ObjectSetInteger(0, name, OBJPROP_COLOR, backgroundColor);
| |||
ObjectSetInteger(0, name, OBJPROP_FILL, true);
| |||
}
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Static in-class variables |
| |||
//+------------------------------------------------------------------+
| |||
static Shape::Registrator *Shape::Registrator::regs[] = {};
| |||
| |||
static MyRegistrator<Rectangle> Rectangle::r;
| |||
static MyRegistrator<Square> Square::r;
| |||
static MyRegistrator<Ellipse> Ellipse::r;
| |||
static MyRegistrator<Circle> Circle::r;
| |||
static MyRegistrator<Triangle> Triangle::r;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Return random number in a range |
| |||
//+------------------------------------------------------------------+
| |||
int random(int range)
| |||
{
| |||
return (int)(rand() / 32767.0 * range);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Create a random shape |
| |||
//+------------------------------------------------------------------+
| |||
Shape *addRandomShape()
| |||
{
| |||
const int w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
| |||
const int h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
| |||
| |||
const int n = random(Shape::Registrator::getTypeCount());
| |||
| |||
int cx = 1 + w / 4 + random(w / 2), cy = 1 + h / 4 + random(h / 2);
| |||
int clr = ((random(256) << 16) | (random(256) << 8) | random(256));
| |||
| |||
int custom[] = {1 + random(w / 4), 1 + random(h / 4)};
| |||
return Shape::Registrator::get(n).create(cx, cy, clr, custom);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Script program start function |
| |||
//+------------------------------------------------------------------+
| |||
void OnStart()
| |||
{
| |||
// adjust chart settings
| |||
const int scale = (int)ChartGetInteger(0, CHART_SCALE);
| |||
ChartSetInteger(0, CHART_SCALEFIX_11, true);
| |||
ChartSetInteger(0, CHART_SCALE, 0); // most compressed view: 1 bar = 1 pixel
| |||
ChartSetInteger(0, CHART_SHOW, false);
| |||
ChartRedraw();
| |||
| |||
AutoPtr<Shape> shapes[FIGURES];
| |||
| |||
// prepare a random set of shapes for drawing
| |||
for(int i = 0; i < FIGURES; ++i)
| |||
{
| |||
Shape *shape = shapes[i] = addRandomShape();
| |||
shape.draw();
| |||
}
| |||
| |||
ChartRedraw(); // show initial state
| |||
| |||
const int n = Shape::Registrator::getTypeCount();
| |||
for(int i = 0; i < n; ++i)
| |||
{
| |||
Print(Shape::Registrator::get(i).type, " ",
| |||
Shape::Registrator::get(i).getShapeCount());
| |||
}
| |||
| |||
// keep small random movement of shapes until user stops it
| |||
while(!IsStopped())
| |||
{
| |||
Sleep(250);
| |||
for(int i = 0; i < FIGURES; ++i)
| |||
{
| |||
shapes[i][].move(random(20) - 10, random(20) - 10);
| |||
shapes[i][].draw();
| |||
}
| |||
ChartRedraw();
| |||
}
| |||
| |||
ChartSetInteger(0, CHART_SCALEFIX, false); // CHART_SCALEFIX_11
| |||
ChartSetInteger(0, CHART_SCALE, scale);
| |||
ChartSetInteger(0, CHART_SHOW, true);
| |||
}
| |||
//+------------------------------------------------------------------+
|