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);
| |
}
| |
//+------------------------------------------------------------------+
|