//+------------------------------------------------------------------+ //| Canvas.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include #include #define SIGN(i) ((i<0) ? -1 : 1) //+------------------------------------------------------------------+ //| Macro to generate color | //+------------------------------------------------------------------+ #define XRGB(r,g,b) (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b)) #define ARGB(a,r,g,b) ((uchar(a)<<24)|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b)) #define TRGB(a,rgb) ((uchar(a)<<24)|(rgb)) #define GETRGB(clr) ((clr)&0xFFFFFF) #define GETRGBA(clr) uchar((clr)>>24) #define GETRGBR(clr) uchar((clr)>>16) #define GETRGBG(clr) uchar((clr)>>8) #define GETRGBB(clr) uchar(clr) #define COLOR2RGB(clr) (0xFF000000|(uchar(clr)<<16)|(uchar((clr)>>8)<<8)|uchar((clr)>>16)) #define RGB2COLOR(rgb) ((uchar(rgb)<<16)|(uchar((rgb)>>8)<<8)|uchar((rgb)>>16)) //+------------------------------------------------------------------+ //| Line end style (round, butt, square) | //+------------------------------------------------------------------+ enum ENUM_LINE_END { LINE_END_ROUND, LINE_END_BUTT, LINE_END_SQUARE, }; //+------------------------------------------------------------------+ //| Class CCanvas | //| Usage: class for working with a dynamic resource | //+------------------------------------------------------------------+ class CCanvas { private: uint m_style; // line style template uint m_style_idx; // variable - current index of bit in line style template static uint m_default_colors[9]; // default colors protected: long m_chart_id; // chart ID string m_objname; // object name ENUM_OBJECT m_objtype; // object type string m_rcname; // resource name int m_width; // canvas width int m_height; // canvas height ENUM_COLOR_FORMAT m_format; // method of color processing //--- for text string m_fontname; // font name int m_fontsize; // font size uint m_fontflags; // font flags uint m_fontangle; // angle of text tilt to the X axis in 0.1 degrees //--- data uint m_pixels[]; // array of pixels public: CCanvas(void); ~CCanvas(void); //--- create/attach/destroy virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); bool CreateBitmap(const string name,const datetime time,const double price, const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); bool CreateBitmap(const long chart_id,const int subwin,const string name, const datetime time,const double price,const int width,const int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); bool CreateBitmapLabel(const string name,const int x,const int y, const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); bool CreateBitmapLabel(const long chart_id,const int subwin,const string name, const int x,const int y,const int width,const int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); virtual bool Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); virtual bool Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); virtual void Destroy(void); //--- properties string ChartObjectName(void) const { return(m_objname); } string ResourceName(void) const { return(m_rcname); } int Width(void) const { return(m_width); } int Height(void) const { return(m_height); } //--- update object on screen void Update(const bool redraw=true); bool Resize(const int width,const int height); //--- clear/fill color void Erase(const uint clr=0); //--- data access uint PixelGet(const int x,const int y) const; void PixelSet(const int x,const int y,const uint clr); //--- draw primitives void LineVertical(int x,int y1,int y2,const uint clr); void LineHorizontal(int x1,int x2,int y,const uint clr); void Line(int x1,int y1,int x2,int y2,const uint clr); void Polyline(int &x[],int &y[],const uint clr); void Polygon(int &x[],int &y[],const uint clr); void Rectangle(int x1,int y1,int x2,int y2,const uint clr); void Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); void Circle(int x,int y,int r,const uint clr); void Ellipse(int x1,int y1,int x2,int y2,const uint clr); void Arc(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr); void Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr); void Arc(int x,int y,int rx,int ry,double fi3,double fi4,int &x3,int &y3,int &x4,int &y4,const uint clr); void Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr); void Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr); //--- draw filled primitives void FillRectangle(int x1,int y1,int x2,int y2,const uint clr); void FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); void FillPolygon(int &x[],int &y[],const uint clr); void FillCircle(int x,int y,int r,const uint clr); void FillEllipse(int x1,int y1,int x2,int y2,const uint clr); void Fill(int x,int y,const uint clr); void Fill(int x,int y,const uint clr,const uint threshould); //--- draw primitives with antialiasing void PixelSetAA(const double x,const double y,const uint clr); void LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX); void PolylineAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX); void PolygonAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX); void TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3, const uint clr,const uint style=UINT_MAX); void CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX); void EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX); //--- draw primitives with antialiasing by Wu's algorithm void LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX); void PolylineWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX); void PolygonWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX); void TriangleWu(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX); void CircleWu(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX); void EllipseWu(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX); //--- draw primitives with prefiltered antialiasing void LineThickVertical(const int x,const int y1,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style); void LineThickHorizontal(const int x1,const int x2,const int y,const uint clr,const int size,const uint style,ENUM_LINE_END end_style); void LineThick(const int x1,const int y1,const int x2,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style); void PolylineThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style); void PolygonThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style); //--- draw primitives smoothing polyline and polygon void PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size, ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND, double tension=0.5,double step=10); void PolygonSmooth(int &x[],int &y[],const uint clr,const int size, ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND, double tension=0.5,double step=10); //--- BitBlt void BitBlt(int dst_x,int dst_y,const uint &src[],int src_width,int src_height,int src_x,int src_y,int src_dx,int src_dy,uint mode=0); //--- for text bool FontSet(const string name,const int size,const uint flags=0,const uint angle=0); bool FontNameSet(string name); bool FontSizeSet(int size); bool FontFlagsSet(uint flags); bool FontAngleSet(uint angle); void FontGet(string &name,int &size,uint &flags,uint &angle); string FontNameGet(void) const { return(m_fontname); } int FontSizeGet(void) const { return(m_fontsize); } uint FontFlagsGet(void) const { return(m_fontflags); } uint FontAngleGet(void) const { return(m_fontangle); } void TextOut(int x,int y,string text,const uint clr,uint alignment=0); int TextWidth(const string text); int TextHeight(const string text); void TextSize(const string text,int &width,int &height); //--- services static uint GetDefaultColor(const int i); void TransparentLevelSet(const uchar value); //--- load bitmap from file bool LoadFromFile(const string filename); //--- line style property uint LineStyleGet(void) const; void LineStyleSet(const uint style); //--- load bitmap from file to buffer static bool LoadBitmap(const string filename,uint &data[],int &width,int &height); private: bool FontSet(void); void TextOutFast(int x,int y,string text,const uint clr,uint alignment=0); bool PixelsSimilar(const uint clr0,const uint clr1,const uint threshould); //--- for Wu's algorithm void PixelTransform(const int x,const int y,const uint clr,const double alpha); //--- for circle and ellipse void PixelTransform4(const int x,const int y,const int dx,const int dy,const uint clr,const double alpha); void PixelSet4AA(const double x,const double y,const double dx,const double dy,const uint clr); //--- for thick line void SegmentVertical(const int x,const int y1,const int y2,const int ysign,const double r,const uint clr,ENUM_LINE_END end_style); void SegmentHorizontal(const int x1,const int x2,const int y,const int xsign,const double r,const uint clr,ENUM_LINE_END end_style); void Segment(const int x1,const int y1,const int x2,const int y2,const double kp0,const double kp1,const int xsign,const int ysign, const double rcos_k,const double rsin_k,const double r,const uint clr,ENUM_LINE_END end_style); double DistancePointSegment(const double px,const double py,const double x1,const double y1,const double x2,const double y2); //--- for pie double AngleCalc(int x1,int y1,int x2,int y2); //--- for polygon int PointClassify(const CPoint &p0,const CPoint &p1,const CPoint &p2); int PolygonClassify(const CPoint &p[]); bool IsPolygonConvex(CPoint &p[]); void PolygonNormalize(CPoint &p[]); void PolygonIntersect(CPoint &p[],CPoint &add[]); void PolygonFill(CPoint &p[],const uint clr); //--- for smoothing polyline and polygon void CalcCurveBezierEndp(const double xend,const double yend,const double xadj,const double yadj,const double tension,double &x,double &y); void CalcCurveBezier(const int &x[],const int &y[],const int i,const double tension,double &x1,double &y1,double &x2,double &y2); double CalcBezierX(const double t,const double x0,const double x1,const double x2,const double x3); double CalcBezierY(const double t,const double y0,const double y1,const double y2,const double y3); protected: //--- method for prefiltered antialiasing virtual double FilterFunction(const double x); }; //+------------------------------------------------------------------+ //| Initialize static array | //+------------------------------------------------------------------+ uint CCanvas::m_default_colors[9]= { XRGB(0,0,255), // blue XRGB(255,0,0), // red XRGB(0,128,0), // green XRGB(255,242,0), // yellow XRGB(255,0,128), // pink XRGB(0,255,0), // lime XRGB(185,0,61), // crimson XRGB(0,183,239), // sky blue XRGB(255,128,0) // orange }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CCanvas::CCanvas(void) : m_chart_id(0), m_objname(NULL), m_objtype(WRONG_VALUE), m_rcname(NULL), m_width(0), m_height(0), m_format(COLOR_FORMAT_XRGB_NOALPHA), m_fontname("arial"), m_fontsize(-120), m_fontflags(0), m_fontangle(0), m_style(UINT_MAX), m_style_idx(0) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CCanvas::~CCanvas(void) { } //+------------------------------------------------------------------+ //| Create dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { Destroy(); //--- prepare data array if(width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) { //--- generate uniq resource name string uniq=(string)ChartID()+(string)GetTickCount()+"."+(string)(GetMicrosecondCount()&0x3FF); m_rcname="::" + StringSubstr(name,0,63-StringLen(uniq))+uniq; //--- initialize data with zeros ArrayInitialize(m_pixels,0); //--- create dynamic resource if(ResourceCreate(m_rcname,m_pixels,width,height,0,0,0,clrfmt)) { //--- successfully created //--- complete initialization m_width =width; m_height=height; m_format=clrfmt; //--- succeed return(true); } } //--- error - destroy object Destroy(); return(false); } //+------------------------------------------------------------------+ //| Create object on chart with attached dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::CreateBitmap(const string name,const datetime time,const double price, const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { return(CreateBitmap(0,0,name,time,price,width,height,clrfmt)); } //+------------------------------------------------------------------+ //| Create object on chart with attached dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::CreateBitmap(const long chart_id,const int subwin,const string name, const datetime time,const double price,const int width,const int height, ENUM_COLOR_FORMAT clrfmt) { //--- create canvas if(Create(name,width,height,clrfmt)) { //--- create attached object if(ObjectCreate(chart_id,name,OBJ_BITMAP,subwin,time,price)) { //--- bind object with resource if(ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname)) { //--- successfully created //--- complete initialization m_chart_id=chart_id; m_objname =name; m_objtype =OBJ_BITMAP; //--- succeed return(true); } } } //--- error return(false); } //+------------------------------------------------------------------+ //| Create object on chart with attached dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::CreateBitmapLabel(const string name,const int x,const int y, const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt)); } //+------------------------------------------------------------------+ //| Create object on chart with attached dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::CreateBitmapLabel(const long chart_id,const int subwin,const string name, const int x,const int y,const int width,const int height, ENUM_COLOR_FORMAT clrfmt) { //--- create canvas if(Create(name,width,height,clrfmt)) { //--- create attached object if(ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwin,0,0)) { //--- set x,y and bind object with resource if(ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x) && ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y) && ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname)) { //--- successfully created //--- complete initialization m_chart_id=chart_id; m_objname =name; m_objtype =OBJ_BITMAP_LABEL; //--- succeed return(true); } } } //--- error return(false); } //+------------------------------------------------------------------+ //| Attach new object with bitmap resource | //+------------------------------------------------------------------+ bool CCanvas::Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) { if(OBJ_BITMAP_LABEL==ObjectGetInteger(chart_id,objname,OBJPROP_TYPE)) { string rcname=ObjectGetString(chart_id,objname,OBJPROP_BMPFILE); rcname=StringSubstr(rcname,StringFind(rcname,"::")); if(ResourceReadImage(rcname,m_pixels,(uint&)m_width,(uint&)m_height)) { m_chart_id=chart_id; m_objname=objname; m_rcname=rcname; m_format=clrfmt; m_objtype=OBJ_BITMAP_LABEL; //--- success return(true); } } //--- failed return(false); } //+------------------------------------------------------------------+ //| Attach new object without bitmap resource | //+------------------------------------------------------------------+ bool CCanvas::Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) { if(OBJ_BITMAP_LABEL==ObjectGetInteger(chart_id,objname,OBJPROP_TYPE)) { string rcname=ObjectGetString(chart_id,objname,OBJPROP_BMPFILE); if(StringLen(rcname)==0 && width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) { ZeroMemory(m_pixels); if(ResourceCreate("::"+objname,m_pixels,width,height,0,0,0,clrfmt) && ObjectSetString(chart_id,objname,OBJPROP_BMPFILE,"::"+objname)) { m_chart_id=chart_id; m_width=width; m_height=height; m_objname=objname; m_rcname="::"+objname; m_format=clrfmt; m_objtype=OBJ_BITMAP_LABEL; //--- success return(true); } } } //--- failed return(false); } //+------------------------------------------------------------------+ //| Remove object from chart and deallocate data array | //+------------------------------------------------------------------+ void CCanvas::Destroy(void) { //--- delete object if(m_objname!=NULL) { ObjectDelete(m_chart_id,m_objname); m_chart_id=0; m_objname =NULL; m_objtype =WRONG_VALUE; } //--- deallocate array ArrayFree(m_pixels); //--- free resource if(m_rcname!=NULL) { ResourceFree(m_rcname); m_rcname=NULL; } //--- zeroize data m_width =0; m_height=0; } //+------------------------------------------------------------------+ //| Update object on screen (redraw) | //+------------------------------------------------------------------+ void CCanvas::Update(const bool redraw) { //--- check if(m_rcname==NULL) return; //--- update resource and redraw if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format) && redraw) ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+ //| Resize | //+------------------------------------------------------------------+ bool CCanvas::Resize(const int width,const int height) { //--- check if(m_rcname!=NULL && width>0 && height>0) if(ArrayResize(m_pixels,width*height)>0) { m_width =width; m_height=height; //--- initialize data with zeros ArrayInitialize(m_pixels,0); //--- create dynamic resource if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format)) { //--- bind object with resource if(m_objname!=NULL && ObjectSetString(m_chart_id,m_objname,OBJPROP_BMPFILE,m_rcname)) return(true); } } //--- error return(false); } //+------------------------------------------------------------------+ //| Clear/Fill color | //+------------------------------------------------------------------+ void CCanvas::Erase(const uint clr) { ArrayInitialize(m_pixels,clr); } //+------------------------------------------------------------------+ //| Get pixel color | //+------------------------------------------------------------------+ uint CCanvas::PixelGet(const int x,const int y) const { //--- check coordinates if(x>=0 && x=0 && y=0 && x=0 && y=m_width || y<0 || y>=m_height) return; //--- int index=y*m_width+x; uint old_clr=m_pixels[index]; //--- check if replacement is necessary if(old_clr==clr) return; //--- use pseudo stack to emulate deeply-nested recursive calls int stack[]; uint count=1; int idx; int total=ArraySize(m_pixels); //--- allocate memory for stack if(ArrayResize(stack,total)==-1) return; stack[0]=index; m_pixels[index]=clr; for(uint i=0; i0 && m_pixels[idx]==old_clr) { m_pixels[idx]=clr; stack[count++]=idx; } //--- top adjacent point idx=index-m_width; if(idx>=0 && m_pixels[idx]==old_clr) { m_pixels[idx]=clr; stack[count++]=idx; } //--- right adjacent point idx=index+1; if(x=m_width || y<0 || y>=m_height || threshould>255) return; //--- int index=y*m_width+x; uint old_clr=m_pixels[index]; //--- check if replacement is necessary if(old_clr==clr) return; //--- use pseudo stack to emulate deeply-nested recursive calls int stack[]; uint count=1; int idx; int total=ArraySize(m_pixels); //--- allocate memory for stack if(ArrayResize(stack,total)==-1) return; stack[0]=index; m_pixels[index]=clr; for(uint i=0; i0 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr) { m_pixels[idx]=clr; stack[count++]=idx; } //--- top adjacent point idx=index-m_width; if(idx>=0 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr) { m_pixels[idx]=clr; stack[count++]=idx; } //--- right adjacent point idx=index+1; if(xy2) { tmp=y1; y1 =y2; y2 =tmp; } //--- line is out of image boundaries if(y2<0 || y1>=m_height || x<0 || x>=m_width) return; //--- stay withing image boundaries if(y1<0) y1=0; if(y2>=m_height) y2=m_height-1; //--- draw line int index=y1*m_width+x; for(int i=y1; i<=y2; i++,index+=m_width) m_pixels[index]=clr; } //+------------------------------------------------------------------+ //| Draw horizontal line | //+------------------------------------------------------------------+ void CCanvas::LineHorizontal(int x1,int x2,int y,const uint clr) { int tmp; //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- line is out of image boundaries if(x2<0 || x1>=m_width || y<0 || y>=m_height) return; //--- stay withing image boundaries if(x1<0) x1=0; if(x2>=m_width) x2=m_width-1; //--- draw line ArrayFill(m_pixels,y*m_width+x1,(x2-x1)+1,clr); } //+------------------------------------------------------------------+ //| Draw line according to Bresenham's algorithm | //+------------------------------------------------------------------+ void CCanvas::Line(int x1,int y1,int x2,int y2,const uint clr) { //--- line is out of image boundaries if((x1<0 && x2<0) || (y1<0 && y2<0)) return; if(x1>=m_width && x2>=m_width) return; if(y1>=m_height && y2>=m_height) return; //--- get length by X and Y int dx=(x2>x1)? x2-x1 : x1-x2; int dy=(y2>y1)? y2-y1 : y1-y2; if(dx==0) { //--- vertical line LineVertical(x1,y1,y2,clr); return; } if(dy==0) { //--- horizontal line LineHorizontal(x1,x2,y1,clr); return; } //--- get direction by X and Y int sx=(x1=m_width || y1<0 || y1>=m_height) { if(draw) return; } else { //--- draw pixel m_pixels[y1*m_width+x1]=clr; draw=true; } //--- get coordinates of next pixel int er2=er<<1; if(er2>-dy) { er-=dy; x1+=sx; } if(er2ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; total--; //--- draw for(int i=0; iArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; total--; //--- draw for(int i=0; i=dx) { xx=x+dx; if(xx>=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0) { dy--; dd_y+=2; f+=dd_y; } dx++; dd_x+=2; f+=dd_x; } } //+------------------------------------------------------------------+ //| Draw ellipse according to Bresenham's algorithm | //+------------------------------------------------------------------+ void CCanvas::Ellipse(int x1,int y1,int x2,int y2,const uint clr) { int x,y; int rx,ry; int dx,dy; int xx,yy; int rx_sq,ry_sq; int f; int tmp; //--- handle extreme conditions if(x1==x2) { if(y1==y2) PixelSet(x1,y1,clr); else LineVertical(x1,y1,y2,clr); return; } if(y1==y2) { LineHorizontal(x1,x2,y1,clr); return; } //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- sort by Y if(y1>y2) { tmp=y1; y1 =y2; y2 =tmp; } x =(x2+x1)>>1; y =(y2+y1)>>1; rx=(x2-x1)>>1; ry=(y2-y1)>>1; dx=0; dy=ry; rx_sq=rx*rx; ry_sq=ry*ry; f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); while(rx_sq*dy>ry_sq*dx) { yy=y+dy; if(yy>=0 && yy=0 && xx=0 && xx=0 && yy=0 && xx=0 && xx=0) { dy--; f-=(rx_sq<<2)*dy; } f+=(ry_sq<<1)*(3+(dx<<1)); dx++; } f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq; while(dy>=0) { yy=y+dy; if(yy>=0 && yy=0 && xx=0 && xx=0 && yy=0 && xx=0 && xxx2) { tmp=x1; x1 =x2; x2 =tmp; } //--- sort by Y if(y1>y2) { tmp=y1; y1 =y2; y2 =tmp; } x =(x2+x1)>>1; y =(y2+y1)>>1; //--- check rays if(x3==x && y3==y) return; if(x4==x && y4==y) return; //--- calculate parameters of ray x3,y3 fi3=AngleCalc(x,y,x3,y3); //--- calculate parameters of ray x4,y4 fi4=AngleCalc(x,y,x4,y4); //--- draw arc Arc(x,y,x2-x,y2-y,fi3,fi4,clr); } //+------------------------------------------------------------------+ //| Draws ellipse arc | //+------------------------------------------------------------------+ void CCanvas::Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr) { int x3,y3,x4,y4; //--- check if(rx<10 || ry<10) return; if(rx<0) rx=-rx; if(ry<0) ry=-ry; //--- check rays if(fi3==fi4) return; //--- adjustment for passing through 0 if(fi40) || // ray 3 is in the 1st or 2nd quadrant (fi0) || // ray 4 is in the 1st or 2nd quadrant (fi4-fi3>=M_PI)) // arc will pass through the top of the ellipse { dx=0; dy=ry; f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); while(rx_sq*dy>=ry_sq*dx) { yy=y-dy; if(dx==0) { //--- central point fi=AngleCalc(0,0,0,-dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(x,yy,clr); ckw=ackw=true; } else ckw=ackw=false; xx_c=x; yy_c=yy; fi_c=fi; xx_a=x; yy_a=yy; fi_a=fi; } else { //--- iterate clockwise xx=x+dx; fi=AngleCalc(0,0,dx,-dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 if(!ckw) { ckw=true; if(!ray4) { if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) PixelSet(x3=xx,y3=yy,clr); else { x3=xx_c; y3=yy_c; } ray3=true; } ckw=false; } //--- save parameters of the last iteration xx_c=xx; yy_c=yy; fi_c=fi; //--- iterate counterclockwise xx=x-dx; fi=AngleCalc(0,0,-dx,-dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 if(!ackw) { ackw=true; if(!ray3) { if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=xx,y4=yy,clr); else { x4=xx_a; y4=yy_a; } ray4=true; } ackw=false; } //--- save parameters of the last iteration xx_a=xx; yy_a=yy; fi_a=fi; } //--- calculate coordinates of the next point if(f>=0) { dy--; f-=(rx_sq<<2)*dy; } f+=(ry_sq<<1)*(3+(dx<<1)); dx++; } //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found if(ckw && !ray3) { fi=AngleCalc(0,0,dx,-dy); if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) PixelSet(x3=x+dx,y3=y-dy,clr); else { x3=xx_c; y3=yy_c; } } //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found if(ackw && !ray4) { fi=AngleCalc(0,0,-dx,-dy); if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=x-dx,y4=y-dy,clr); else { x4=xx_a; y4=yy_a; } } } //--- 2 left //--- //--- if arc is obviously not within the rays range, don't draw fi=MathMod(fi4,2*M_PI); if((fi3>M_PI_2 && fi3<3*M_PI_2) || // ray 3 is in the 2nd or 3rd quadrant (fi>M_PI_2 && fi<3*M_PI_2) || // ray 4 is in the 2nd or 3rd quadrant (fi4-fi3>=M_PI)) // arc will pass through the left part of the ellipse { dx=rx; dy=0; f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq); while(ry_sq*dx>=rx_sq*dy) { xx=x-dx; if(dy==0) { //--- central point fi=AngleCalc(0,0,-dx,0); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,y,clr); ckw=ackw=true; } else ckw=ackw=false; xx_c=xx; yy_c=y; fi_c=fi; xx_a=xx; yy_a=y; fi_a=fi; } else { //--- iterate clockwise yy=y-dy; fi=AngleCalc(0,0,-dx,-dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 if(!ckw) { ckw=true; if(!ray4) { if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) PixelSet(x3=xx,y3=yy,clr); else { x3=xx_c; y3=yy_c; } ray3=true; } ckw=false; } //--- save parameters of the last iteration xx_c=xx; yy_c=yy; fi_c=fi; //--- iterate counterclockwise yy=y+dy; fi=AngleCalc(0,0,-dx,dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 if(!ackw) { ackw=true; if(!ray3) { if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=xx,y4=yy,clr); else { x4=xx_a; y4=yy_a; } ray4=true; } ackw=false; } //--- save parameters of the last iteration xx_a=xx; yy_a=yy; fi_a=fi; } //--- calculate coordinates of the next point if(f>=0) { dx--; f-=(ry_sq<<2)*dx; } f+=(rx_sq<<1)*(3+(dy<<1)); dy++; } //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found if(ckw && !ray3) { fi=AngleCalc(0,0,-dx,-dy); if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) PixelSet(x3=x-dx,y3=y-dy,clr); else { x3=xx_c; y3=yy_c; } } //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found if(ackw && !ray4) { fi=AngleCalc(0,0,-dx,dy); if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=x-dx,y4=y+dy,clr); else { x4=xx_a; y4=yy_a; } } } //--- 3 bottom //--- //--- if arc is obviously not within the rays range, don't draw fi=MathMod(fi4,2*M_PI); if((fi3>M_PI && fi3<2*M_PI) || // ray 3 is in the 3rd or 4th quadrant (fi>M_PI && fi<2*M_PI) || // ray 4 is in the 3rd or 4th quadrant (fi4-fi3>=M_PI)) // arc will pass through the bottom of the ellipse { dx=0; dy=ry; f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); while(rx_sq*dy>=ry_sq*dx) { yy=y+dy; if(dx==0) { //--- central point fi=AngleCalc(0,0,0,dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(x,yy,clr); ckw=ackw=true; } else ckw=ackw=false; xx_c=x; yy_c=yy; fi_c=fi; xx_a=x; yy_a=yy; fi_a=fi; } else { //--- iterate clockwise xx=x-dx; fi=AngleCalc(0,0,-dx,dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 if(!ckw) { ckw=true; if(!ray4) { if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) PixelSet(x3=xx,y3=yy,clr); else { x3=xx_c; y3=yy_c; } ray3=true; } ckw=false; } //--- save parameters of the last iteration xx_c=xx; yy_c=yy; fi_c=fi; //--- iterate counterclockwise xx=x+dx; fi=AngleCalc(0,0,dx,dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 if(!ackw) { ackw=true; if(!ray3) { if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=xx,y4=yy,clr); else { x4=xx_a; y4=yy_a; } ray4=true; } ackw=false; } //--- save parameters of the last iteration xx_a=xx; yy_a=yy; fi_a=fi; } //--- calculate coordinates of the next point if(f>=0) { dy--; f-=(rx_sq<<2)*dy; } f+=(ry_sq<<1)*(3+(dx<<1)); dx++; } //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found if(ckw && !ray3) { fi=AngleCalc(0,0,-dx,dy); if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) PixelSet(x3=x-dx,y3=y+dy,clr); else { x3=xx_c; y3=yy_c; } } //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found if(ackw && !ray4) { fi=AngleCalc(0,0,dx,dy); if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=x+dx,y4=y+dy,clr); else { x4=xx_a; y4=yy_a; } } } //--- 4 right //--- //--- if arc is obviously not within the rays range, don't draw fi=MathMod(fi4,2*M_PI); if((fi33*M_PI_2) || // ray 3 is 1 or 4 quadrant (fi3*M_PI_2) || // ray 4 is 1 or 4 quadrant (fi4-fi3>=M_PI)) // arc will pass through the right side of the ellipse { dx=rx; dy=0; f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq); while(ry_sq*dx>=rx_sq*dy) { xx=x+dx; if(dy==0) { //--- central point fi=AngleCalc(0,0,dx,0); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,y,clr); ckw=ackw=true; } else ckw=ackw=false; xx_c=xx; yy_c=y; fi_c=fi; xx_a=xx; yy_a=y; fi_a=fi; } else { //--- iterate clockwise yy=y+dy; fi=AngleCalc(0,0,dx,dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 if(!ckw) { ckw=true; if(!ray4) { if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) PixelSet(x3=xx,y3=yy,clr); else { x3=xx_c; y3=yy_c; } ray3=true; } ckw=false; } //--- save parameters of the last iteration xx_c=xx; yy_c=yy; fi_c=fi; //--- iterate counterclockwise yy=y-dy; fi=AngleCalc(0,0,dx,-dy); if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) { PixelSet(xx,yy,clr); //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 if(!ackw) { ackw=true; if(!ray3) { if(MathAbs(MathMod(fi_a,2*M_PI)-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=xx,y4=yy,clr); else { x4=xx_a; y4=yy_a; } ray4=true; } ackw=false; } //--- save parameters of the last iteration xx_a=xx; yy_a=yy; fi_a=fi; } //--- calculate coordinates of the next point if(f>=0) { dx--; f-=(ry_sq<<2)*dx; } f+=(rx_sq<<1)*(3+(dy<<1)); dy++; } //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found if(ckw && !ray3) { fi=AngleCalc(0,0,dx,dy); if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) PixelSet(x3=x+dx,y3=y+dy,clr); else { x3=xx_c; y3=yy_c; } } //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found if(ackw && !ray4) { fi=AngleCalc(0,0,dx,-dy); if(MathAbs(MathMod(fi_a,2*M_PI)-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) PixelSet(x4=x+dx,y4=y-dy,clr); else { x4=xx_a; y4=yy_a; } } } } //+------------------------------------------------------------------+ //| Draws ellipse pie | //+------------------------------------------------------------------+ void CCanvas::Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr) { int tmp; int x,y; //--- double fi3; double fi4; //--- check if(x1==x2 || y1==y2) return; //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- sort by Y if(y1>y2) { tmp=y1; y1 =y2; y2 =tmp; } x =(x2+x1)>>1; y =(y2+y1)>>1; //--- check rays if(x3==x && y3==y) return; if(x4==x && y4==y) return; //--- calculate parameters of ray x3,y3 fi3=AngleCalc(x,y,x3,y3); //--- calculate parameters of ray x4,y4 fi4=AngleCalc(x,y,x4,y4); //--- draw pie Pie(x,y,x2-x,y2-y,fi3,fi4,clr,fill_clr); } //+------------------------------------------------------------------+ //| Draws ellipse pie | //+------------------------------------------------------------------+ void CCanvas::Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr) { int x3=x; int y3=y; int x4=x; int y4=y; //--- check if(rx==0 || ry==0) return; if(rx<0) rx=-rx; if(ry<0) ry=-ry; //--- check rays if(fi3==fi4) return; //--- adjustment for passing through 0 if(fi4ry) rx=ry; double fi=(fi3+fi4)/2; int xf=x+(int)(0.9*rx*cos(fi)); int yf=y-(int)(0.9*rx*sin(fi)); Fill(xf,yf,fill_clr); } //+------------------------------------------------------------------+ //| Draw filled circle | //+------------------------------------------------------------------+ void CCanvas::FillCircle(int x,int y,int r,const uint clr) { int f =1-r; int dd_x=1; int dd_y=-2*r; int dx =0; int dy =r; //--- draw while(dy>=dx) { LineHorizontal(x-dy,x+dy,y-dx,clr); LineHorizontal(x-dy,x+dy,y+dx,clr); //--- if(f>=0) { LineHorizontal(x-dx,x+dx,y-dy,clr); LineHorizontal(x-dx,x+dx,y+dy,clr); dy--; dd_y+=2; f+=dd_y; } dx++; dd_x+=2; f+=dd_x; } } //+------------------------------------------------------------------+ //| Draw filled ellipse | //+------------------------------------------------------------------+ void CCanvas::FillEllipse(int x1,int y1,int x2,int y2,const uint clr) { int x,y; int rx,ry; int dx,dy; int rx_sq,ry_sq; int f; int tmp; //--- handle extreme conditions if(x1==x2) { if(y1==y2) PixelSet(x1,y1,clr); else LineVertical(x1,y1,y2,clr); return; } if(y1==y2) { LineHorizontal(x1,x2,y1,clr); return; } //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- sort by Y if(y1>y2) { tmp=y1; y1 =y2; y2 =tmp; } x =(x2+x1)>>1; y =(y2+y1)>>1; rx=(x2-x1)>>1; ry=(y2-y1)>>1; dx=0; dy=ry; rx_sq=rx*rx; ry_sq=ry*ry; f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); while(rx_sq*dy>ry_sq*(dx)) { LineHorizontal(x-dx,x+dx,y+dy,clr); LineHorizontal(x-dx,x+dx,y-dy,clr); if(f>=0) { dy--; f-=(rx_sq<<2)*dy; } f+=(ry_sq<<1)*(3+(dx<<1)); dx++; } f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq; while(dy>=0) { LineHorizontal(x-dx,x+dx,y+dy,clr); LineHorizontal(x-dx,x+dx,y-dy,clr); if(f<=0) { dx++; f+=(ry_sq<<2)*dx; } dy--; f+=(rx_sq<<1)*(3-(dy<<1)); } } //+------------------------------------------------------------------+ //| Draw filled rectangle | //+------------------------------------------------------------------+ void CCanvas::FillRectangle(int x1,int y1,int x2,int y2,const uint clr) { int tmp; //--- sort vertexes if(x2=m_width || y1>=m_height) return; //--- stay withing screen boundaries if(x1<0) x1=0; if(y1<0) y1=0; if(x2>=m_width) x2=m_width -1; if(y2>=m_height) y2=m_height-1; int len=(x2-x1)+1; //--- set pixels for(; y1<=y2; y1++) ArrayFill(m_pixels,y1*m_width+x1,len,clr); } //+------------------------------------------------------------------+ //| Draw filled triangle | //+------------------------------------------------------------------+ void CCanvas::FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr) { int xx1,xx2,tmp; double k1=0,k2=0,xd1,xd2; //--- sort vertexes from lesser to greater if(y1>y2) { tmp=y2; y2 =y1; y1 =tmp; tmp=x2; x2 =x1; x1=tmp; } if(y1>y3) { tmp=y1; y1 =y3; y3 =tmp; tmp=x1; x1 =x3; x3 =tmp; } if(y2>y3) { tmp=y2; y2 =y3; y3 =tmp; tmp=x2; x2 =x3; x3 =tmp; } //--- all vertexes are out of image boundaries if(y3<0 || y1>m_height) return; if(x1<0 && x2<0 && x3<0) return; if(x1>m_width && x2>m_width && x3>m_width) return; //--- find coefficients of lines if((tmp=y1-y2)!=0) k1=(x1-x2)/(double)tmp; if((tmp=y1-y3)!=0) k2=(x1-x3)/(double)tmp; //--- xd1=x1; xd2=x1; //--- for(int i=y1; i<=y3; i++) { if(i==y2) { if((tmp=y2-y3)!=0) k1=(x2-x3)/(double)tmp; xd1=x2; } //--- calculate new boundaries of triangle line xx1 =(int)xd1; xd1+=k1; xx2 =(int)xd2; xd2+=k2; //--- triangle line is out of screen boundaries if(i<0 || i>=m_height) continue; //--- sort if(xx1>xx2) { tmp=xx1; xx1=xx2; xx2=tmp; } //--- line is out of screen boundaries if(xx2<0 || xx1>=m_width) continue; //--- draw only what is within screen boundaries if(xx1<0) xx1=0; if(xx2>=m_width) xx2=m_width-1; //--- draw horizontal line of triangle ArrayFill(m_pixels,i*m_width+xx1,xx2-xx1,clr); } } //+------------------------------------------------------------------+ //| Draw filled poligon | //+------------------------------------------------------------------+ void CCanvas::FillPolygon(int &x[],int &y[],const uint clr) { static CPoint p[]; int total=ArraySize(x); if(total>ArraySize(y)) total=ArraySize(y); //--- check if(total<3) return; //--- resize array of points ArrayResize(p,total); //--- find top-left point int imin=0; int xmin=x[0]; int ymin=y[0]; for(int i=1; iymin) continue; if(y[i]==ymin) { if(x[i]0.0) xx[1]=xx[3]=ix+1; if(dy<0.0) yy[2]=yy[3]=iy-1; if(dy==0.0) yy[2]=yy[3]=iy; if(dy>0.0) yy[2]=yy[3]=iy+1; //--- calculate radii and sum of their squares for(int i=0; i<4; i++) { dx=xx[i]-x; dy=yy[i]-y; rr[i]=1/(dx*dx+dy*dy); rrr+=rr[i]; } //--- draw pixels for(int i=0; i<4; i++) { k=rr[i]/rrr; c=PixelGet(xx[i],yy[i]); a=(uchar)(k*GETRGBA(clr)+(1-k)*GETRGBA(c)); r=(uchar)(k*GETRGBR(clr)+(1-k)*GETRGBR(c)); g=(uchar)(k*GETRGBG(clr)+(1-k)*GETRGBG(c)); b=(uchar)(k*GETRGBB(clr)+(1-k)*GETRGBB(c)); PixelSet(xx[i],yy[i],ARGB(a,r,g,b)); } } //+------------------------------------------------------------------+ //| Get line style | //+------------------------------------------------------------------+ uint CCanvas::LineStyleGet(void) const { switch(m_style) { case 0xFFFFFF: return(STYLE_SOLID); break; case 0x3FFFF: return(STYLE_DASH); break; case 0x1C71C7: return(STYLE_DOT); break; case 0x381FF: return(STYLE_DASHDOT); break; case 0x1C71FF: return(STYLE_DASHDOTDOT); break; default: return (m_style); break; } } //+------------------------------------------------------------------+ //| Set line style | //+------------------------------------------------------------------+ void CCanvas::LineStyleSet(const uint style) { switch(style) { case STYLE_SOLID: m_style=0xFFFFFF; break; case STYLE_DASH: m_style=0x3FFFF; break; case STYLE_DOT: m_style=0x1C71C7; break; case STYLE_DASHDOT: m_style=0x381FF; break; case STYLE_DASHDOTDOT: m_style=0x1C71FF; break; default: //--- high-order bit must be set then custom style if((style&0x80000000)!=0) { m_style=style; } break; } m_style_idx=0; } //+------------------------------------------------------------------+ //| Draw line with antialiasing (with style) | //+------------------------------------------------------------------+ void CCanvas::LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style) { //--- line is out of image boundaries if((x1<0 && x2<0) || (y1<0 && y2<0)) return; if(x1>=m_width && x2>=m_width) return; if(y1>=m_height && y2>=m_height) return; //--- check if(x1==x2 && y1==y2) { PixelSet(x1,y1,clr); return; } //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); //--- preliminary calculations double dx=x2-x1; double dy=y2-y1; double xy=sqrt(dx*dx+dy*dy); double xx=x1; double yy=y1; uint mask=1<=fabs(dx) && fabs(y2-yy)>=fabs(dy)); //--- set last pixel if((m_style&mask)==mask) { PixelSetAA(x2,y2,clr); } //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; } //+------------------------------------------------------------------+ //| Draw polyline with antialiasing (with style) | //+------------------------------------------------------------------+ void CCanvas::PolylineAA(int &x[],int &y[],const uint clr,const uint style) { //--- check arrays int total=ArraySize(x); if(total>ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; total--; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<=m_width && x2>=m_width) { //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } if(y1>=m_height && y2>=m_height) { //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } //--- check if(x1==x2 && y1==y2) { PixelSet(x1,y1,clr); //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } //--- preliminary calculations double dx=x2-x1; double dy=y2-y1; double xy=sqrt(dx*dx+dy*dy); double xx=x1; double yy=y1; //--- set pixels dx/=xy; dy/=xy; do { if((m_style&mask)==mask) { PixelSetAA(xx,yy,clr); } xx+=dx; yy+=dy; mask<<=1; if(mask==0x1000000) mask=1; } while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy)); //--- set last pixel if((m_style&mask)==mask) { PixelSetAA(x2,y2,clr); } mask<<=1; if(mask==0x1000000) mask=1; } //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; } //+------------------------------------------------------------------+ //| Draw polygon with antialiasing (with style) | //+------------------------------------------------------------------+ void CCanvas::PolygonAA(int &x[],int &y[],const uint clr,const uint style) { //--- check arrays int total=ArraySize(x); if(total>ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<=m_width && x2>=m_width) { //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } if(y1>=m_height && y2>=m_height) { //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } //--- check if(x1==x2 && y1==y2) { PixelSet(x1,y1,clr); //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } //--- preliminary calculations double dx=x2-x1; double dy=y2-y1; double xy=sqrt(dx*dx+dy*dy); double xx=x1; double yy=y1; //--- set pixels dx/=xy; dy/=xy; do { if((m_style&mask)==mask) { PixelSetAA(xx,yy,clr); } xx+=dx; yy+=dy; mask<<=1; if(mask==0x1000000) mask=1; } while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy)); //--- set last pixel if((m_style&mask)==mask) { PixelSetAA(x2,y2,clr); } } //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; } //+------------------------------------------------------------------+ //| Draw triangle with antialiasing | //+------------------------------------------------------------------+ void CCanvas::TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style) { //--- draw int x[3]; int y[3]; x[0] = x1; x[1] = x2; x[2] = x3; y[0] = y1; y[1] = y2; y[2] = y3; PolygonAA(x,y,clr,style); } //+------------------------------------------------------------------+ //| Draw circle with antialiasing | //+------------------------------------------------------------------+ void CCanvas::CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX) { if(r<=0) return; //--- preliminary calculations double xx=x+r; double yy=y; double fi=0; double df=M_PI_2/MathCeil(r); //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<M_PI) df/=2; do { xx=x+r*cos(fi); yy=y-r*sin(fi); if((m_style&mask)==mask) PixelSetAA(xx,yy,clr); mask<<=1; if(mask==0x1000000) mask=1; fi+=df; } while(fabs(2*M_PI-fi)>=df/2); //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; } //+------------------------------------------------------------------+ //| Draw ellipse with antialiasing | //+------------------------------------------------------------------+ void CCanvas::EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX) { double rx = (x2-x1)/2; double ry = (y2-y1)/2; //--- preliminary calculations double x=(x2>x1) ? x1+rx : x2+rx; double y=(y2>y1) ? y1+ry : y2+ry; double rx2=rx*rx; double ry2=ry*ry; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<ym) continue; if((p[i].y==ym) && (p[i].x>xm)) continue; im=i; xm=p[i].x; ym=p[i].y; } //--- check the orientation of triangle return PointClassify(p[(im-1+total)%total],p[im],p[(im+1)%total]); } //+------------------------------------------------------------------+ //| Checks convexity of polygon | //+------------------------------------------------------------------+ bool CCanvas::IsPolygonConvex(CPoint &p[]) { int total=ArraySize(p); //--- triangle - always convex if(total==3) return(true); int res=SIGN(PointClassify(p[0],p[1],p[2])); for(int i=1; iymin) continue; if(p[i].y==ymin) { if(p[i].xp[il].y) return; if(yy!=p[il].y) { dl=(p[il].x-xl)/(p[il].y-yy); //--- make adjustment for half of left increment dl/2 LineHorizontal((int)MathCeil(xl+dl/2),(int)MathFloor(xl),yy,clr); xl+=dl/2; } else LineHorizontal((int)MathCeil(xl),(int)MathFloor(p[il].x),yy,clr); } while(yy==p[ir].y) { ir=(ir+1)%total; if(yy>p[ir].y) return; if(yy!=p[ir].y) { dr=(p[ir].x-xr)/(p[ir].y-yy); //--- make adjustment for half of right increment dr/2 LineHorizontal((int)MathCeil(xr),(int)MathFloor(xr+dr/2),yy,clr); xr+=dr/2; } else LineHorizontal((int)MathCeil(p[ir].x),(int)MathFloor(xr),yy,clr); } yy++; if(yy==p[il].y) xl=p[il].x; else xl+=dl; if(yy==p[ir].y) xr=p[ir].x; else xr+=dr; LineHorizontal((int)MathCeil(xl),(int)MathFloor(xr),yy,clr); } while(il>=ir && ir!=0); } //+------------------------------------------------------------------+ //| Draw line according to Wu's algorithm | //+------------------------------------------------------------------+ void CCanvas::LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX) { //--- calculating the variation of the coordinates int dx = (x2 > x1) ? (x2 - x1) : (x1 - x2); int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2); //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<y2) { tmp=y1; y1 =y2; y2 =tmp; } //--- line is out of image boundaries if(y2<0 || y1>=m_height || x1<0 || x1>=m_width) { //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } //--- stay withing image boundaries if(y1<0) y1=0; if(y2>=m_height-1) y2=m_height-1; //--- draw line int index=y1*m_width+x1; for(int i=y1; i<=y2; i++,index+=m_width) { if((m_style&mask)==mask) m_pixels[index]=clr; mask<<=1; if(mask==0x1000000) mask=1; } //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; //--- success return; } //--- check if dy==0 then draw horizontal line if(dy==0) { //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- line is out of image boundaries if(x2<0 || x1>=m_width || y1<0 || y1>=m_height) { //--- set the previous line style if(style!=UINT_MAX) m_style=prev_style; return; } //--- stay withing image boundaries if(x1<0) x1=0; if(x2>=m_width) x2=m_width-1; //--- draw line for(int i=0,index=y1*m_width+x1; i 1) else { //--- first point has to have a smaller Y coordinate if(y2ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; total--; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1< x1) ? (x2 - x1) : (x1 - x2); int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2); int tmp; //--- check if dx==0 then draw vertical line if(dx==0) { //--- sort by Y if(y1>y2) { tmp=y1; y1 =y2; y2 =tmp; } //--- line is out of image boundaries if(y2<0 || y1>=m_height || x1<0 || x1>=m_width) continue; //--- stay withing image boundaries if(y1<0) y1=0; if(y2>=m_height-1) y2=m_height-1; //--- draw line int index=y1*m_width+x1; for(int j=y1; j<=y2; j++,index+=m_width) { if((m_style&mask)==mask) m_pixels[index]=clr; mask<<=1; if(mask==0x1000000) mask=1; } continue; } //--- check if dy==0 then draw horizontal line if(dy==0) { //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- line is out of image boundaries if(x2<0 || x1>=m_width || y1<0 || y1>=m_height) continue; //--- stay withing image boundaries if(x1<0) x1=0; if(x2>=m_width) x2=m_width-1; //--- draw line for(int j=0,index=y1*m_width+x1; j 1) else { //--- first point has to have a smaller Y coordinate if(y2ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1< x1) ? (x2 - x1) : (x1 - x2); int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2); int tmp; //--- check if dx==0 then draw vertical line if(dx==0) { //--- sort by Y if(y1>y2) { tmp=y1; y1 =y2; y2 =tmp; } //--- line is out of image boundaries if(y2<0 || y1>=m_height || x1<0 || x1>=m_width) continue; //--- stay withing image boundaries if(y1<0) y1=0; if(y2>=m_height-1) y2=m_height-1; //--- draw line int index=y1*m_width+x1; for(int j=y1; j<=y2; j++,index+=m_width) { if((m_style&mask)==mask) m_pixels[index]=clr; mask<<=1; if(mask==0x1000000) mask=1; } continue; } //--- check if dy==0 then draw horizontal line if(dy==0) { //--- sort by X if(x1>x2) { tmp=x1; x1 =x2; x2 =tmp; } //--- line is out of image boundaries if(x2<0 || x1>=m_width || y1<0 || y1>=m_height) continue; //--- stay withing image boundaries if(x1<0) x1=0; if(x2>=m_width) x2=m_width-1; //--- draw line for(int j=0,index=y1*m_width+x1; j 1) else { //--- first point has to have a smaller Y coordinate if(y2x1) ? x1+rx : x2+rx; int y=(y2>y1) ? y1+ry : y2+ry; if(rx<=0 || ry<=0) return; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<0) LineWu(x,y1,x,y2,clr,style); return; } //--- r be the filter radius (and also the half-width of the wide line) double r=(size/2.0); //--- primary calculate int dy=MathAbs(y2-y1); int sign=(y10) LineWu(x1,y,x2,y,clr,style); return; } //--- r be the filter radius (and also the half-width of the wide line) double r=(size/2.0); //--- primary calculate int dx=MathAbs(x2-x1); int sign=(x10) LineWu(x1,y1,x2,y2,clr,style); return; } //--- r be the filter radius (and also the half-width of the wide line) double r=(size/2.0); //--- compute x and y deltas double dx=MathAbs(x2-x1); double dy=MathAbs(y2-y1); if(dx==0) { LineThickVertical(x1,y1,y2,clr,size,style,end_style); return; } if(dy==0) { LineThickHorizontal(x1,x2,y1,clr,size,style,end_style); return; } //--- compute the linear coefficients of the two (scaled) edge functions double k=MathArctan(dx/dy); double rcos_k=r*cos(k); double rsin_k=r*sin(k); //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<0) PolylineWu(x,y,clr,style); return; } //--- check arrays int total=ArraySize(x); if(total>ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; total--; //--- r be the filter radius (and also the half-width of the wide line) double r=(size/2.0); //--- double gap=1.0; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<0) PolylineWu(x,y,clr,style); return; } //--- check arrays int total=ArraySize(x); if(total>ArraySize(y)) total=ArraySize(y); //--- check if(total<2) return; //--- r be the filter radius (and also the half-width of the wide line) double r=(size/2.0); //--- double gap=1.0; //--- set the line style uint prev_style=m_style; if(style!=UINT_MAX) LineStyleSet(style); uint mask=1<>16) &0xff) - uint((clr1>>16) &0xff)); uint dg=MathAbs(uint((clr0>>8) &0xff) - uint((clr1>>8) &0xff)); uint db=MathAbs(uint((clr0>>0) &0xff) - uint((clr1>>0) &0xff)); //--- return return (dr<=threshould || dg<=threshould || db<=threshould); } //+------------------------------------------------------------------+ //| Calculate and set new color | //+------------------------------------------------------------------+ void CCanvas::PixelTransform(const int x,const int y,const uint clr,const double alpha) { int index=y*m_width+x; //--- check if(x<0 || y<0 || x>m_width || y>m_height || index>=ArraySize(m_pixels)) return; //--- check alpha if(alpha==1) { m_pixels[index]=clr; return; } //--- get pixel color uint clr0=m_pixels[index]; //--- transform of color component for the background double r0 = ((clr0>>16) & 0xFF) * (1.0-alpha); double g0 = ((clr0>>8) & 0xFF) * (1.0-alpha); double b0 = ((clr0>>0) & 0xFF) * (1.0-alpha); //--- transform of color component double r1 = ((clr>>16) & 0xFF) * (alpha); double g1 = ((clr>>8) & 0xFF) * (alpha); double b1 = ((clr>>0) & 0xFF) * (alpha); //--- components of the new color int r = (int)(r0+r1); int g = (int)(g0+g1); int b = (int)(b0+b1); //--- set new color m_pixels[y*m_width+x]=((r<<16)|(g<<8)|(b<<0)|(255<<24)); } //+------------------------------------------------------------------+ //| Draw 4 pixel with PixelTransform method | //+------------------------------------------------------------------+ void CCanvas::PixelTransform4(const int x,const int y,const int dx,const int dy,const uint clr,const double alpha) { PixelTransform(x+dx,y+dy,clr,alpha); PixelTransform(x-dx,y+dy,clr,alpha); PixelTransform(x+dx,y-dy,clr,alpha); PixelTransform(x-dx,y-dy,clr,alpha); } //+------------------------------------------------------------------+ //| Draw 4 pixel with antialiasing | //+------------------------------------------------------------------+ void CCanvas::PixelSet4AA(const double x,const double y,const double dx,const double dy,const uint clr) { PixelSetAA(x+dx,y+dy,clr); PixelSetAA(x-dx,y+dy,clr); PixelSetAA(x+dx,y-dy,clr); PixelSetAA(x-dx,y-dy,clr); } //+------------------------------------------------------------------+ //| Draw solid segment for vertical thick line | //+------------------------------------------------------------------+ void CCanvas::SegmentVertical(const int x,const int y1,const int y2,const int ysign,const double r,const uint clr,ENUM_LINE_END end_style) { //--- compute the constol points of the solid segment int ye1,ye2; int ys1,ys2; switch(end_style) { case LINE_END_ROUND: { ye1=y1; ye2=y2; ys1=y1-(int)(ysign*r); ys2=y2+(int)(ysign*r); break; } case LINE_END_BUTT: { ye1=y1; ye2=y2; ys1=y1; ys2=y2; break; } case LINE_END_SQUARE: { ye1=y1-(int)(ysign*r); ye2=y2+(int)(ysign*r); ys1=ye1; ys2=ye2; break; } default: return; }; //--- darw solid segment for(int i=0; i<=MathAbs(ys2-ys1); i++) { double yi=ys1+(ysign*i); for(int j=0; j<2*r; j++) { double xi=x-r+j; double dist=DistancePointSegment(xi,yi,x,ye1,x,ye2); double val=MathAbs(dist/r); PixelTransform((int)xi,(int)yi,clr,FilterFunction(val)); } } } //+------------------------------------------------------------------+ //| Draw solid segment for horizontal thick line | //+------------------------------------------------------------------+ void CCanvas::SegmentHorizontal(const int x1,const int x2,const int y,const int xsign,const double r,const uint clr,ENUM_LINE_END end_style) { //--- compute the constol points of the solid segment int xe1,xe2; int xs1,xs2; switch(end_style) { case LINE_END_ROUND: { xe1=x1; xe2=x2; xs1=x1-(int)(xsign*r); xs2=x2+(int)(xsign*r); break; } case LINE_END_BUTT: { xe1=x1; xe2=x2; xs1=x1; xs2=x2; break; } case LINE_END_SQUARE: { xe1=x1-(int)(xsign*r); xe2=x2+(int)(xsign*r); xs1=xe1; xs2=xe2; break; } default: return; }; //--- draw solid segment for(int i=0; i<=MathAbs(xs2-xs1); i++) { double xi=xs1+(xsign*i); for(int j=0; j<2*r; j++) { double yi=y-r+j; double dist=DistancePointSegment(xi,yi,xe1,y,xe2,y); double val=MathAbs(dist/r); PixelTransform((int)xi,(int)yi,clr,FilterFunction(val)); } } } //+------------------------------------------------------------------+ //| Draw solid segment for thick line | //+------------------------------------------------------------------+ void CCanvas::Segment(const int x1,const int y1,const int x2,const int y2,const double kp0,const double kp1,const int xsign,const int ysign, const double rcos_k,const double rsin_k,const double r,const uint clr,ENUM_LINE_END end_style) { if(x1==x2 && y1==y2) return; if(x1==x2) { SegmentVertical(x1,y1,y2,ysign,r,clr,end_style); return; } if(y1==y2) { SegmentHorizontal(x1,x2,y1,xsign,r,clr,end_style); return; } //--- compute the constol points of the solid segment int xe1,ye1,xe2,ye2; int xs1,ys1,xs2,ys2; switch(end_style) { case LINE_END_ROUND: { xe1=x1; ye1=y1; xe2=x2; ye2=y2; xs1=x1-(xsign)*(int)(rsin_k); ys1=y1-(ysign)*(int)(rcos_k); xs2=x2+(xsign)*(int)(rsin_k); ys2=y2+(ysign)*(int)(rcos_k); break; } case LINE_END_BUTT: { xe1=x1; ye1=y1; xe2=x2; ye2=y2; xs1=x1; ys1=y1; xs2=x2; ys2=y2; break; } case LINE_END_SQUARE: { xe1=x1-(xsign)*(int)(rsin_k); ye1=y1-(ysign)*(int)(rcos_k); xe2=x2+(xsign)*(int)(rsin_k); ye2=y2+(ysign)*(int)(rcos_k); xs1=xe1; ys1=ye1; xs2=xe2; ys2=ye2; break; } default: return; }; //--- compute the four corners of the wide line double p0x=xs1+(xsign)*(rcos_k); double p0y=ys1-(ysign)*(rsin_k); double p1x=xs1-(xsign)*(rcos_k); double p1y=ys1+(ysign)*(rsin_k); double p2x=xs2+(xsign)*(rcos_k); double p2y=ys2-(ysign)*(rsin_k); double p3x=xs2-(xsign)*(rcos_k); double p3y=ys2+(ysign)*(rsin_k); //--- draw solid segment if(MathAbs(kp0)>=1) { double xi0,xi1; double height=MathAbs(p3y-p0y); for(int i=0; i<=height; i++) { double y=p0y+(ysign*i); double xi00 = MathRound(p0x + (y-p0y)/kp0); double xi01 = MathRound(p1x + (y-p1y)/kp1); double xi02 = MathRound(p2x + (y-p2y)/kp1); double xi03 = MathRound(p3x + (y-p3y)/kp0); if(xsign==1) { xi0 = MathMax(xi00,xi01); xi1 = MathMin(xi02,xi03); } else { xi0 = MathMin(xi00,xi01); xi1 = MathMax(xi02,xi03); } double width=MathAbs(MathRound(xi1-xi0)); for(int j=0; j<=width; j++) { double xi=xi0+(xsign*j); double dist=DistancePointSegment(xi,y,xe1,ye1,xe2,ye2); double val = MathAbs(dist/r); PixelTransform((int)xi,(int)y,clr,FilterFunction(val)); } } } else { double yi0,yi1; double width=MathAbs(p2x-p1x); for(int i=0; i<=width; i++) { double x=p1x+(xsign*i); double yi00 = MathRound(p0y + (x-p0x)*kp0); double yi01 = MathRound(p1y + (x-p1x)*kp1); double yi02 = MathRound(p2y + (x-p2x)*kp1); double yi03 = MathRound(p3y + (x-p3x)*kp0); if(ysign==1) { yi0 = MathMax(yi00,yi02); yi1 = MathMin(yi01,yi03); } else { yi0 = MathMin(yi00,yi02); yi1 = MathMax(yi01,yi03); } double height=MathAbs(yi1-yi0); for(int j=0; j<=height; j++) { double yi=yi0+(ysign*j); double dist=DistancePointSegment(x,yi,xe1,ye1,xe2,ye2); double val=MathAbs(dist/r); PixelTransform((int)x,(int)yi,clr,FilterFunction(val)); } } } } //+------------------------------------------------------------------+ //| Filter function for calculating alpha channel | //+------------------------------------------------------------------+ double CCanvas::FilterFunction(const double x) { if(x<=0.8) return(1.0); else return MathExp(-(x-0.8)*(x-0.8)*50); } //+------------------------------------------------------------------+ //| Calculate distance between point and segment | //+------------------------------------------------------------------+ double CCanvas::DistancePointSegment(const double px,const double py,const double x1,const double y1,const double x2,const double y2) { //--- primary calculate double a=(px-x1)*(px-x1)+(py-y1)*(py-y1); double b=(px-x2)*(px-x2)+(py-y2)*(py-y2); double c=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1); //--- check if(a>=b+c) return (MathSqrt(b)); if(b>=a+c) return (MathSqrt(a)); //--- calculate distance a=MathSqrt(a); b=MathSqrt(b); c=MathSqrt(c); double p=(a+b+c)/2; double s=MathSqrt((p-a)*(p-b)*(p-c)*p); //--- check distance if(MathIsValidNumber(s)) return(s*2.0/c); else return(0); } //+------------------------------------------------------------------+ //| Draw smothing polyline | //+------------------------------------------------------------------+ void CCanvas::PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,ENUM_LINE_STYLE style=STYLE_SOLID, ENUM_LINE_END end_style=LINE_END_ROUND,double tension=0.5,double step=10) { //--- int arr_size= ArraySize(x); if(arr_size!=ArraySize(y)) return; //--- double x1,x2,y1,y2; tension*=0.3; //--- coordinates of Bezier curve int xc[]; int yc[]; //--- initialize control points double ptX[]; double ptY[]; int size_pt=arr_size*3-2; ArrayResize(ptX,size_pt); ArrayResize(ptY,size_pt); //--- calculation of control points CalcCurveBezierEndp(x[0],y[0],x[1],y[1],tension,x1,y1); ptX[0] = x[0]; ptY[0] = y[0]; ptX[1] = x1; ptY[1] = y1; for(int i=0; i0.0) ?(int)(distance/step) : 1; if(size_i<1) size_i=2; ArrayResize(xc,ArraySize(xc)+size_i,1024); ArrayResize(yc,ArraySize(yc)+size_i,1024); for(int t=0; t0.0) ?(int)(distance/step) : 1; if(size_i<1) size_i=2; ArrayResize(xc,ArraySize(xc)+size_i,1024); ArrayResize(yc,ArraySize(yc)+size_i,1024); for(int t=0; t0.0) ?(int)(distance/step) : 1; if(size_i<1) size_i=2; ArrayResize(xc,ArraySize(xc)+size_i,1024); ArrayResize(yc,ArraySize(yc)+size_i,1024); for(int t=0; t