diff --git a/uppsrc/ScatterDraw/DrawingFunctions.h b/uppsrc/ScatterDraw/DrawingFunctions.h new file mode 100644 index 000000000..be7a769ca --- /dev/null +++ b/uppsrc/ScatterDraw/DrawingFunctions.h @@ -0,0 +1,25 @@ +#ifndef _ScatterDraw_DrawingFunctions_h_ +#define _ScatterDraw_DrawingFunctions_h_ + +void DrawLine(Draw &w, double x0, double y0, double x1, double y1, double width, const Color &color); +void DrawLine(Painter &w, double x0, double y0, double x1, double y1, double width, const Color &color); +void DrawLineOpa(Draw& w, const int x0, const int y0, const int x1, const int y1, const int &scale, const double opacity, double thick, const Color &_color, String pattern, const Color &background = Null); +void DrawLineOpa(Painter& w, const int x0, const int y0, const int x1, const int y1, const int &scale, const double opacity, double thick, const Color &_color, String pattern, const Color &background = Null); +void FillRectangleOpa(Draw &w, double x0, double y0, double x1, double y1, const int &scale, const double opacity, const Color &background, const Color &color); +void FillRectangleOpa(Painter &w, double x0, double y0, double x1, double y1, const int &scale, const double opacity, const Color &background, const Color &color); +void DrawPolylineOpa(Draw& w, const Vector &p, const int &scale, const double opacity, double thick, const Color &color, String pattern = "", const Color &background = Null); +void DrawPolylineOpa(Painter& w, const Vector &p, const int &scale, const double opacity, double thick, const Color &color, String pattern = "", const Color &background = Null); +void FillPolylineOpa(Draw& w, const Vector &p, const int &scale, const double opacity, const Color &background, const Color &fillColor); +void FillPolylineOpa(Painter& w, const Vector &p, const int &scale, const double opacity, const Color &background, const Color &fillColor); +void DrawVArrow(Draw &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color); +void DrawVArrow(Painter &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color); +void DrawHArrow(Draw &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color); +void DrawHArrow(Painter &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color); +void DrawText(Draw &w, double x, double y, int angle, const String &text, Font font, Color color); +void DrawText(Painter &w, double x, double y, int angle, const String &text, Font font, Color color); +void Clip(Draw &w, double x, double y, double cx, double cy); +void Clip(Painter &w, double x, double y, double cx, double cy); +void ClipEnd(Draw &w); +void ClipEnd(Painter &w); + +#endif diff --git a/uppsrc/ScatterDraw/MarkPlot.h b/uppsrc/ScatterDraw/MarkPlot.h new file mode 100644 index 000000000..bc6373d60 --- /dev/null +++ b/uppsrc/ScatterDraw/MarkPlot.h @@ -0,0 +1,181 @@ +#ifndef _ScatterDraw_MarkPlot_h_ +#define _ScatterDraw_MarkPlot_h_ + +using namespace Upp; + +class MarkPlot { +public: + virtual ~MarkPlot() {}; + virtual void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const {}; + virtual void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const {}; + template + static void Register(const String& name) + { + classMap().FindAdd(name, __Create); + typeMap().FindAdd(typeid(T).name(), name); + } + static void Unregister(const String& name) + { + int i = TypeIndex(name); + ASSERT(i >= 0); + classMap().Remove(i); + typeMap().Remove(i); + } + static String TypeName(int i) {return typeMap()[i];} + static int TypeIndex(const String& name) {return max(classMap().Find(name),0);} + static int GetCount() {return classMap().GetCount();} + static MarkPlot* Create(int i) {return classMap()[i]();} + static Vector GetTypes() {return VectorMap(typeMap(),1).GetValues();} + int GetType() const {return typeMap().Find(typeid(*this).name());} + MarkPlot* Copy()const; + +protected: + typedef MarkPlot* (*CreateFunc)(); + template + static MarkPlot* __Create() {return new T;} + static VectorMap& classMap() {static VectorMap cMap; return cMap;} + static VectorMap& typeMap() {static VectorMap tMap; return tMap;} +}; + +class CircleMarkPlot : public MarkPlot { +private: + template + void DoPaint(T& w, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + int radius = fround(scale*size); + int radius2 = radius/2; + w.DrawEllipse(cp.x - radius2, cp.y - radius2, radius, radius, markColor, 1, markColor); + } + +public: + void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } + void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } +}; + +class SquareMarkPlot : public MarkPlot { +private: + template + void DoPaint(T& w, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + Vector p; + int side2l = int((size*scale)/2.); + int side2r = int(size*scale - side2l); + p << Point(cp.x - side2l, cp.y - side2l) << Point(cp.x + side2r, cp.y - side2l) + << Point(cp.x + side2r, cp.y + side2r) << Point(cp.x - side2l, cp.y + side2r) + << Point(cp.x - side2l, cp.y - side2l); + w.DrawPolygon(p, markColor, scale/2, markColor); + } + +public: + void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } + void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } +}; + +class TriangleMarkPlot : public MarkPlot { +private: + template + void DoPaint(T& w, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + Vector p; + int xl = int((size*scale)/2.); + int xr = int(size*scale - xl); + int yt = int(size*scale*2/3.); + int yb = int(size*scale - yt); + p << Point(cp.x - xl, cp.y + yb) << Point(cp.x + xr, cp.y + yb) + << Point(cp.x, cp.y - yt) << Point(cp.x - xl, cp.y + yb); + w.DrawPolygon(p, markColor, scale/2, markColor); + } + +public: + void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } + void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } +}; + +class CrossMarkPlot : public MarkPlot { +private: + template + void DoPaint(T& w, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + int side2l = int((size*scale)/2.); + int side2r = int(size*scale - side2l); + w.DrawLine(cp.x - side2l, cp.y, cp.x + side2r, cp.y, scale, markColor); + w.DrawLine(cp.x, cp.y - side2l, cp.x, cp.y + side2r, scale, markColor); + } + +public: + void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } + void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } +}; + +class XMarkPlot : public MarkPlot { +private: + template + void DoPaint(T& w, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + int side2l = int((size*scale)/2.); + int side2r = int(size*scale - side2l); + w.DrawLine(cp.x - side2l, cp.y - side2l, cp.x + side2r, cp.y + side2r, scale, markColor); + w.DrawLine(cp.x + side2r, cp.y - side2l, cp.x - side2l, cp.y + side2r, scale, markColor); + } + +public: + void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } + void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } +}; + +class RhombMarkPlot : public MarkPlot { +private: + template + void DoPaint(T& w, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + Vector p; + int side2l = int((size*scale)/2.); + int side2r = int(size*scale - side2l); + p << Point(cp.x, cp.y - side2l) << Point(cp.x + side2r, cp.y) + << Point(cp.x, cp.y + side2r) << Point(cp.x - side2l, cp.y) + << Point(cp.x, cp.y - side2l); + w.DrawPolygon(p, markColor, scale/2, markColor); + } + +public: + void Paint(Draw &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } + void Paint(Painter &p, const int& scale, const Point& cp, const double& size, const Color& markColor) const + { + DoPaint(p, scale, cp, size, markColor); + } +}; + +#endif diff --git a/uppsrc/ScatterDraw/ScatterDraw.cpp b/uppsrc/ScatterDraw/ScatterDraw.cpp new file mode 100644 index 000000000..9bb97aa70 --- /dev/null +++ b/uppsrc/ScatterDraw/ScatterDraw.cpp @@ -0,0 +1,1407 @@ +#include "ScatterDraw.h" + +ScatterDraw& ScatterDraw::SetColor(const Color& _color) +{ + graphColor = _color; + return *this; +} + +ScatterDraw& ScatterDraw::SetTitle(const String& _title) +{ + title = _title; + return *this; +} + +ScatterDraw& ScatterDraw::SetTitleFont(const Font& fontTitle) +{ + titleFont = fontTitle; + return *this; +} + +ScatterDraw& ScatterDraw::SetTitleColor(const Color& colorTitle) +{ + titleColor = colorTitle; + return *this; +} + +void ScatterDraw::SetLabels(const String& _xLabel, const String& _yLabel, const String& _yLabel2) +{ + xLabel = _xLabel; + yLabel = _yLabel; + yLabel2 = _yLabel2; +} + +ScatterDraw& ScatterDraw::SetLabelX(const String& _xLabel) +{ + xLabel = _xLabel; + return *this; +} + +ScatterDraw& ScatterDraw::SetLabelY(const String& _yLabel) +{ + yLabel = _yLabel; + return *this; +} + +ScatterDraw& ScatterDraw::SetLabelY2(const String& _yLabel) +{ + yLabel2 = _yLabel; + return *this; +} + +ScatterDraw& ScatterDraw::SetLabelsFont(const Font& fontLabels) +{ + labelsFont = fontLabels; + return *this; +} + +ScatterDraw& ScatterDraw::SetLabelsColor(const Color& colorLabels) +{ + labelsColor = colorLabels; + return *this; +} + +ScatterDraw& ScatterDraw::SetPlotAreaMargin(const int hLeft, const int hRight, const int vTop, const int vBottom) +{ + hPlotLeft = hLeft; + hPlotRight = hRight; + vPlotTop = vTop; + vPlotBottom = vBottom; + return *this; +} + +ScatterDraw& ScatterDraw::SetPlotAreaLeftMargin(const int margin) { + hPlotLeft = margin; + return *this; +} + +ScatterDraw& ScatterDraw::SetPlotAreaRightMargin(const int margin) { + hPlotRight = margin; + return *this; +} + +ScatterDraw& ScatterDraw::SetPlotAreaTopMargin(const int margin) { + vPlotTop = margin; + return *this; +} + +ScatterDraw& ScatterDraw::SetPlotAreaBottomMargin(const int margin) { + vPlotBottom = margin; + return *this; +} + +ScatterDraw& ScatterDraw::SetPlotAreaColor(const Color& p_a_color) +{ + plotAreaColor = p_a_color; + return *this; +} + +ScatterDraw& ScatterDraw::SetAxisColor(const Color& axis_color) +{ + axisColor = axis_color; + return *this; +} + +ScatterDraw& ScatterDraw::SetAxisWidth(const int& axis_width) +{ + axisWidth = axis_width; + return *this; +} + +ScatterDraw& ScatterDraw::SetGridColor(const Color& grid_color) +{ + gridColor = grid_color; + return *this; +} + +ScatterDraw& ScatterDraw::SetGridWidth(const int& grid_width) +{ + gridWidth = grid_width; + return *this; +} + +ScatterDraw& ScatterDraw::ShowHGrid(const bool& show) +{ + drawHGrid = show; + return *this; +} + +ScatterDraw& ScatterDraw::ShowVGrid(const bool& show) +{ + drawVGrid = show; + return *this; +} + +ScatterDraw& ScatterDraw::ShowLegend(const bool& show) +{ + showLegend = show; + return *this; +} + +ScatterDraw& ScatterDraw::SetLegendWeight(const int& weight) +{ + legendWeight = weight; + return *this; +} + +ScatterDraw &ScatterDraw::SetDrawXReticle(bool set) +{ + drawXReticle = set; + return *this; +} + +ScatterDraw &ScatterDraw::SetDrawYReticle(bool set) +{ + drawYReticle = set; + return *this; +} + +ScatterDraw &ScatterDraw::SetDrawY2Reticle(bool set) +{ + drawY2Reticle = set; + return *this; +} + +void ScatterDraw::DrawLegend(Draw& w, const int& scale) const +{ + int nmr = fround((GetSize().cx - 2*(hPlotLeft + hPlotRight))/legendWeight); //max number of labels per row + if (nmr <= 0) + return; + int nLab = series.GetCount(); //number of labels + int Nc; //number of complete rows + int LCR; //number of labels on complete row + int R; //number of remaining labels on incomplete row + if(nmr > nLab) { + Nc = 0; LCR = 0; R = nLab; + } else if (nmr == nLab) { + Nc = 1; LCR = nLab; R = 0; + } else { + Nc = nLab/nmr; LCR = nmr; R = nLab%nmr; + } + for(int j = 0; j <= Nc; j++) { + int start = nLab - (j+1)*LCR; + int end = nLab - j*LCR; + if (j == Nc) { + start = 0; + end = R; + } + for(int i = start; i < end; i++) { + Vector vp; + vp << Point(scale*(i-start)*legendWeight, scale*(4-12*(j+1))) << + Point(scale*(i-start)*legendWeight+scale*23, scale*(4-12*(j+1))); + if (series[i].opacity > 0 && series[i].seriesPlot) + DrawPolylineOpa(w, vp, scale, 1, scale*series[i].thickness, series[i].color, series[i].dash); + Point p(scale*((i-start)*legendWeight+7),scale*(4-12*(j+1))/*+scale*Thick.At(i)/12*/); + if (series[i].markWidth >= 1 && series[i].markPlot) + series[i].markPlot->Paint(w, scale, p, series[i].markWidth, series[i].markColor); + Font scaledFont; + scaledFont.Height(scale*StdFont().GetHeight()); + DrawText(w, scale*(i-start)*legendWeight+scale*25, scale*(-2-12*(j+1)), 0, + series[i].legend, scaledFont, series[i].color); + } + } +} + +void ScatterDraw::AdjustMinUnitX() +{ + if (xMajorUnit > 0) { + while (xMinUnit < 0) + xMinUnit += xMajorUnit; + while (xMinUnit > xMajorUnit) + xMinUnit -= xMajorUnit; + } +} +/* +void ScatterDraw::AdjustMinUnitY() +{ + if (yMajorUnit > 0) { + while (yMinUnit < 0) + yMinUnit += yMajorUnit; + while (yMinUnit > yMajorUnit) + yMinUnit -= yMajorUnit; + } +} + +void ScatterDraw::AdjustMinUnitY2() +{ + if (yMajorUnit2 > 0) { + while (yMinUnit2 < 0) + yMinUnit2 += yMajorUnit2; + while (yMinUnit2 > yMajorUnit2) + yMinUnit2 -= yMajorUnit2; + } +} +*/ + +void ScatterDraw::AdjustMinUnitY() +{ + if (yMajorUnit > 0) { + if (fabs(yMinUnit/yMajorUnit) > 10000000000) + yMinUnit = yMajorUnit; + else { + while (yMinUnit < 0) + yMinUnit += yMajorUnit; + } + if (fabs(yMinUnit/yMajorUnit) > 10000000000) + yMinUnit = yMajorUnit; + else { + while (yMinUnit > yMajorUnit) + yMinUnit -= yMajorUnit; + } + } +} + +void ScatterDraw::AdjustMinUnitY2() +{ + if (yMajorUnit2 > 0) { + if (fabs(yMinUnit2/yMajorUnit2) > 10000000000) + yMinUnit2 = yMajorUnit2; + else { + while (yMinUnit2 < 0) + yMinUnit2 += yMajorUnit2; + } + if (fabs(yMinUnit2/yMajorUnit2) > 10000000000) + yMinUnit2 = yMajorUnit2; + else { + while (yMinUnit2 > yMajorUnit2) + yMinUnit2 -= yMajorUnit2; + } + } +} + +ScatterDraw &ScatterDraw::SetRange(double rx, double ry, double ry2) +{ + ASSERT(!(rx <= 0 || ry <= 0 || ry2 <= 0)); + xRange = rx; + yRange = ry; + yRange2 = ry2; + xMajorUnit = xRange/10; + AdjustMinUnitX(); + yMajorUnit = yRange/10; + AdjustMinUnitY(); + yMajorUnit2 = yRange2/10; + AdjustMinUnitY2(); + WhenSetRange(); + return *this; +} + +ScatterDraw &ScatterDraw::SetMajorUnits(double ux, double uy) +{ + if (!IsNull(ux)) { + xMajorUnit = ux; + AdjustMinUnitX(); + } + if (!IsNull(uy)) { + yMajorUnit = uy; + yMajorUnit2 = yMajorUnit*yRange2/yRange; + AdjustMinUnitY(); + AdjustMinUnitY2(); + } + return *this; +} + +ScatterDraw &ScatterDraw::SetMajorUnitsNum(int nx, int ny) +{ + if (!IsNull(nx)) { + xMajorUnit = xRange/nx; + AdjustMinUnitX(); + } + if (!IsNull(ny)) { + yMajorUnit = yRange/ny; + yMajorUnit2 = yMajorUnit*yRange2/yRange; + AdjustMinUnitY(); + AdjustMinUnitY2(); + } + return *this; +} + +ScatterDraw &ScatterDraw::SetMinUnits(double ux, double uy) +{ + xMinUnit = ux; + yMinUnit = uy; + yMinUnit2 = yRange2*yMinUnit/yRange; + AdjustMinUnitX(); + AdjustMinUnitY(); + AdjustMinUnitY2(); + return *this; +} + +ScatterDraw &ScatterDraw::SetXYMin(double xmin, double ymin, double ymin2) +{ + xMin = xmin; + yMin = ymin; + yMin2 = ymin2; + WhenSetXYMin(); + return *this; +} + +void ScatterDraw::FitToData(bool vertical) { + double minx, maxx, miny, miny2, maxy, maxy2; + minx = miny = miny2 = -DOUBLE_NULL; + maxx = maxy = maxy2 = DOUBLE_NULL; + + for (int j = 0; j < series.GetCount(); j++) { + if (series[j].opacity == 0) + continue; + for (int i = 0; i < series[j].PointsData()->GetCount(); i++) { + if (series[j].PointsData()->x(i) < minx) + minx = series[j].PointsData()->x(i); + if (series[j].PointsData()->x(i) > maxx) + maxx = series[j].PointsData()->x(i); + } + } + if (vertical) { + for (int j = 0; j < series.GetCount(); j++) { + if (series[j].opacity == 0) + continue; + for (int i = 0; i < series[j].PointsData()->GetCount(); i++) { + if (series[j].primaryY) { + if (series[j].PointsData()->y(i) < miny) + miny = series[j].PointsData()->y(i); + if (series[j].PointsData()->y(i) > maxy) + maxy = series[j].PointsData()->y(i); + } else { + if (series[j].PointsData()->y(i) < miny2) + miny2 = series[j].PointsData()->y(i); + if (series[j].PointsData()->y(i) > maxy2) + maxy2 = series[j].PointsData()->y(i); + } + } + } + } + if (minx != -DOUBLE_NULL) { + double deltaX = xMin - minx; + xMin -= deltaX; + xMinUnit += deltaX; + AdjustMinUnitX(); + xRange = maxx - minx; + } + if (vertical) { + if (miny != -DOUBLE_NULL) { + /* + if (!IsNull(maxy2)) { + miny = min((miny-yMin)/yRange, (miny2-yMin2)/yRange2); + maxy = max((maxy-yMin-yRange)/yRange, (maxy2-yMin2-yRange2)/yRange2); + } else { + miny = (miny-yMin)/yRange; + maxy = (maxy-yMin-yRange)/yRange; + } + + miny = miny*yRange + yMin; + maxy = maxy*yRange + yMin + yRange; + */ + double fact = yRange2/yRange; + double deltaY = yMin - miny; + double deltaY2 = deltaY*fact; + + yMin -= deltaY; + yMinUnit += deltaY; + AdjustMinUnitY(); + yRange = maxy - miny; + + yMin2 -= deltaY2; + yMinUnit2 += deltaY2; + AdjustMinUnitY2(); + yRange2 = yRange*fact; + } + } + WhenSetRange(); + WhenSetXYMin(); + Refresh(); +} + +ScatterDraw &ScatterDraw::Graduation_FormatX(Formats fi) +{ + switch (fi) { + case EXP: cbModifFormatX = THISBACK(ExpFormat); break; + case MON: cbModifFormatX = THISBACK(MonFormat); break; + case DY: cbModifFormatX = THISBACK(DyFormat); break; + default:break; + } + return *this; +} + +ScatterDraw &ScatterDraw::Graduation_FormatY(Formats fi) +{ + switch (fi) { + case EXP: cbModifFormatY = THISBACK(ExpFormat); break; + case MON: cbModifFormatY = THISBACK(MonFormat); break; + case DY: cbModifFormatY = THISBACK(DyFormat); break; + default:break; + } + return *this; +} + +ScatterDraw &ScatterDraw::Graduation_FormatY2(Formats fi) +{ + switch (fi) { + case EXP: cbModifFormatY2 = THISBACK(ExpFormat); break; + case MON: cbModifFormatY2 = THISBACK(MonFormat); break; + case DY: cbModifFormatY2 = THISBACK(DyFormat); break; + default:break; + } + return *this; +} + +Color ScatterDraw::GetNewColor(int id) +{ + switch(id) { + case 0: return LtBlue(); + case 1: return LtRed(); + case 2: return LtGreen(); + case 3: return Black(); + case 4: return LtGray(); + case 5: return Brown(); + case 6: return Blue(); + case 7: return Red(); + case 8: return Green(); + case 9: return Gray(); + case 10: return LtBlue(); + case 11: return LtRed(); + case 12: return LtGreen(); + case 13: return Black(); + case 14: return LtGray(); + case 15: return Brown(); + case 16: return Blue(); + case 17: return Red(); + case 18: return Green(); + case 19: return Gray(); + } + return Color(Random(), Random(), Random()); +} + +String ScatterDraw::GetNewDash(int id) +{ + switch(id) { + case 0: return LINE_SOLID; + case 1: return LINE_DOTTED; + case 2: return LINE_DASHED; + case 3: return LINE_DASH_DOT; + case 4: return LINE_SOLID; + case 5: return LINE_DOTTED; + case 6: return LINE_DASHED; + case 7: return LINE_DASH_DOT; + case 8: return LINE_SOLID; + case 9: return LINE_DOTTED; + case 10: return LINE_DASHED; + case 11: return LINE_DASH_DOT; + } + dword r = Random(); + if (r < 8000) + r += 2000; + String ret = FormatInt(r).Right(4); + String space = " "; + return ret.Mid(0, 1) + space + ret.Mid(1, 1) + space + ret.Mid(2, 1) + space + ret.Mid(3, 1); +} + +MarkPlot *ScatterDraw::GetNewMarkPlot(int id) +{ + switch(id) { + case 0: return new CircleMarkPlot(); + case 1: return new SquareMarkPlot(); + case 2: return new TriangleMarkPlot(); + case 3: return new CrossMarkPlot(); + case 4: return new XMarkPlot(); + case 5: return new RhombMarkPlot(); + } + return new CircleMarkPlot(); +} + +Color GetOpaqueColor(const Color &color, const Color &background, const double opacity) +{ + if (opacity == 1) + return color; + if (opacity == 0) + return background; + return Color(int(opacity*(color.GetR() - background.GetR()) + background.GetR()), + int(opacity*(color.GetG() - background.GetG()) + background.GetG()), + int(opacity*(color.GetB() - background.GetB()) + background.GetB())); +} + +ScatterDraw::ScatterBasicSeries::ScatterBasicSeries() +{ + color = Null; + thickness = 3; + legend = ""; + opacity = 1; + primaryY = true; + sequential = false; + dash = LINE_SOLID; + seriesPlot = new LineSeriesPlot(); + markPlot = new CircleMarkPlot(); + markWidth = 8; + markColor = Null; + fillColor = Null; +} + +void ScatterDraw::ScatterBasicSeries::Init(int id) +{ + color = GetNewColor(id); + markColor = Color(max(color.GetR()-30, 0), max(color.GetG()-30, 0), max(color.GetB()-30, 0)); + + dash = GetNewDash(id); + markPlot = GetNewMarkPlot(id); +} + +ScatterDraw &ScatterDraw::AddSeries(double *yData, int numData, double x0, double deltaX) +{ + return AddSeries(yData, numData, x0, deltaX); +} + +ScatterDraw &ScatterDraw::AddSeries(double *xData, double *yData, int numData) +{ + return AddSeries(xData, yData, numData); +} + +ScatterDraw &ScatterDraw::AddSeries(Vector &xData, Vector &yData) +{ + return AddSeries(xData, yData); +} + +ScatterDraw &ScatterDraw::AddSeries(Array &xData, Array &yData) +{ + return AddSeries(xData, yData); +} + +ScatterDraw &ScatterDraw::AddSeries(Vector &points) +{ + return AddSeries(points); +} + +ScatterDraw &ScatterDraw::AddSeries(Array &points) +{ + return AddSeries(points); +} + +ScatterDraw &ScatterDraw::AddSeries(double (*function)(double)) +{ + return AddSeries(function); +} + +ScatterDraw &ScatterDraw::AddSeries(Pointf (*function)(double), int np, double from, double to) +{ + return AddSeries(function, np, from, to); +} + +ScatterDraw &ScatterDraw::AddSeries(PlotFunc &function) +{ + return AddSeries(function); +} + +ScatterDraw &ScatterDraw::AddSeries(PlotParamFunc function, int np, double from, double to) +{ + return AddSeries(function, np, from, to); +} + +ScatterDraw &ScatterDraw::AddSeries(DataSource &data) +{ + ScatterSeries &s = series.Add(); + s.Init(series.GetCount()-1); + s.SetDataSource(&data, false); + Refresh(); + return *this; +} + +ScatterDraw &ScatterDraw::_AddSeries(DataSource *data) +{ + ScatterSeries &s = series.Add(); + s.Init(series.GetCount()-1); + s.SetDataSource(data); + Refresh(); + return *this; +} + +void ScatterDraw::InsertSeries(int id, double *yData, int numData, double x0, double deltaX) +{ + InsertSeries(id, yData, numData, x0, deltaX); +} + +void ScatterDraw::InsertSeries(int id, double *xData, double *yData, int numData) +{ + InsertSeries(id, xData, yData, numData); +} + +void ScatterDraw::InsertSeries(int id, Vector &xData, Vector &yData) +{ + InsertSeries(id, xData, yData); +} + +void ScatterDraw::InsertSeries(int id, Array &xData, Array &yData) +{ + InsertSeries(id, xData, yData); +} + +void ScatterDraw::InsertSeries(int id, Vector &points) +{ + InsertSeries(id, points); +} + +void ScatterDraw::InsertSeries(int id, Array &points) +{ + InsertSeries(id, points); +} + +void ScatterDraw::InsertSeries(int id, double (*function)(double)) +{ + InsertSeries(id, function); +} + +void ScatterDraw::InsertSeries(int id, Pointf (*function)(double), int np, double from, double to) +{ + InsertSeries(id, function, np, from, to); +} + +void ScatterDraw::InsertSeries(int id, PlotFunc &function) +{ + InsertSeries(id, function); +} + +void ScatterDraw::InsertSeries(int id, PlotParamFunc function, int np, double from, double to) +{ + InsertSeries(id, function, np, from, to); +} + +void ScatterDraw::_InsertSeries(int id, DataSource *data) +{ + ASSERT(IsValid(id)); + ScatterSeries &s = series.Insert(id); + s.Init(id); + s.SetDataSource(data); + Refresh(); +} + +ScatterDraw &ScatterDraw::PlotStyle(SeriesPlot *data) +{ + int id = series.GetCount() - 1; + + series[id].seriesPlot = data; + return *this; +} + +ScatterDraw &ScatterDraw::MarkStyle(MarkPlot *data) +{ + int id = series.GetCount() - 1; + + series[id].markPlot = data; + return *this; +} + +ScatterDraw &ScatterDraw::Stroke(double thickness, Color color) +{ + int id = series.GetCount() - 1; + + if (IsNull(color)) + color = GetNewColor(id); + series[id].color = color; + series[id].thickness = thickness; + //series[id].dash = GetNewDash(id); + + Refresh(); + return *this; +} + +ScatterDraw &ScatterDraw::Fill(Color color) +{ + int id = series.GetCount() - 1; + + if (IsNull(color)) { + color = GetNewColor(id); + color = Color(min(color.GetR()+30, 255), min(color.GetG()+30, 255), min(color.GetB()+30, 255)); + } + series[id].fillColor = color; + + Refresh(); + return *this; +} + +ScatterDraw &ScatterDraw::MarkColor(Color color) +{ + int id = series.GetCount() - 1; + + if (IsNull(color)) { + color = GetNewColor(id); + color = Color(max(color.GetR()-30, 0), max(color.GetG()-30, 0), max(color.GetB()-30, 0)); + } + series[id].markColor = color; + + Refresh(); + return *this; +} + +ScatterDraw &ScatterDraw::MarkWidth(const double& markWidth) +{ + int id = series.GetCount() - 1; + + series[id].markWidth = markWidth; + + Refresh(); + return *this; +} + +ScatterDraw &ScatterDraw::Dash(const char *dash) +{ + int id = series.GetCount() - 1; + + series[id].dash = dash; + Refresh(); + return *this; +} + +ScatterDraw &ScatterDraw::Legend(const String legend) +{ + int id = series.GetCount() - 1; + + series[id].legend = legend; + return *this; +} + +void ScatterDraw::SetDataColor(const int& j, const Color& color) +{ + ASSERT(IsValid(j)); + series[j].color = color; + Refresh(); +} + +Color ScatterDraw::GetDataColor(const int& j) const +{ + ASSERT(IsValid(j)); + return series[j].color; +} + +void ScatterDraw::SetDataThickness(const int& j, const double& thickness) +{ + ASSERT(IsValid(j)); + series[j].thickness = thickness; + Refresh(); +} + +double ScatterDraw::GetDataThickness(const int& j) const +{ + ASSERT(IsValid(j)); + return series[j].thickness; +} + +void ScatterDraw::SetFillColor(const int& j, const ::Color& color) +{ + ASSERT(IsValid(j)); + series[j].fillColor = color; + Refresh(); +} + +Color ScatterDraw::GetFillColor(const int& j) const +{ + ASSERT(IsValid(j)); + return series[j].fillColor; +} + +ScatterDraw &ScatterDraw::SetMarkWidth(const int& j, const double& markWidth) +{ + ASSERT(IsValid(j)); + series[j].markWidth = markWidth; + Refresh(); + return *this; +} + +double ScatterDraw::GetMarkWidth(const int& j) const +{ + ASSERT(IsValid(j)); + return series[j].markWidth; +} + + +void ScatterDraw::SetMarkColor(const int& j, const ::Color& color) +{ + ASSERT(IsValid(j)); + series[j].markColor = color; + Refresh(); +} + +Color ScatterDraw::GetMarkColor(const int& j) const +{ + ASSERT(IsValid(j)); + return series[j].markColor; +} + +void ScatterDraw::NoMark(const int& j) +{ + ASSERT(IsValid(j)); + series[j].markWidth = 0; +} + +bool ScatterDraw::IsShowMark(const int& j) const throw (Exc) +{ + ASSERT(IsValid(j)); + return series[j].markWidth > 0; +} + + +void ScatterDraw::SetDataPrimaryY(const int& j, const bool& primary) +{ + ASSERT(IsValid(j)); + series[j].primaryY = primary; + Refresh(); +} + +ScatterDraw &ScatterDraw::SetDataPrimaryY(const bool& primary) +{ + SetDataPrimaryY(series.GetCount()-1, primary); + return *this; +} + +bool ScatterDraw::IsDataPrimaryY(const int& j) const throw (Exc) +{ + ASSERT(IsValid(j)); + return series[j].primaryY; +} + +void ScatterDraw::SetSequentialX(const int& j, const bool& sequential) +{ + ASSERT(IsValid(j)); + series[j].sequential = sequential; + Refresh(); +} + +ScatterDraw &ScatterDraw::SetSequentialX(const bool& sequential) +{ + SetSequentialX(series.GetCount()-1, sequential); + return *this; +} + +ScatterDraw &ScatterDraw::SetSequentialXAll(const bool& sequential) +{ + for (int i = 0; i < series.GetCount(); ++i) + SetSequentialX(i, sequential); + sequentialXAll = sequential; + return *this; +} + +void ScatterDraw::Show(const int& j, const bool& show) +{ + ASSERT(IsValid(j)); + series[j].opacity = show ? 1 : 0; + Refresh(); +} + +bool ScatterDraw::IsVisible(const int& j) +{ + ASSERT(IsValid(j)); + return series[j].opacity > 0; +} + +ScatterDraw &ScatterDraw::ShowAll(const bool& show) +{ + for (int i = 0; i < series.GetCount(); ++i) + series[i].opacity = 1; + return *this; +} + +ScatterDraw& ScatterDraw::Id(int id) +{ + return Id(series.GetCount()-1, id); +} + +ScatterDraw& ScatterDraw::Id(const int& j, int id) +{ + ASSERT(IsValid(j)); + series[j].id = id; + return *this; +} + +int ScatterDraw::GetId(const int& j) +{ + ASSERT(IsValid(j)); + return series[j].id; +} + +void ScatterDraw::RemoveSeries(const int& j) +{ + ASSERT(IsValid(j)); + series.Remove(j); + Refresh(); +} + +void ScatterDraw::RemoveAllSeries() +{ + series.Clear(); + Refresh(); +} + +Drawing ScatterDraw::GetDrawing() +{ + DrawingDraw ddw(6*GetSize()); + + SetDrawing (ddw, 6); + return ddw; +} + +Image ScatterDraw::GetImage(const int &scale) +{ +#ifndef flagGUI + ASSERT(mode != MD_DRAW); +#endif + + ImageBuffer ib(scale*GetSize()); + BufferPainter bp(ib, mode); + + SetDrawing(bp, scale); + + return ib; +} + +double ScatterDraw::GetXByPoint(const int x) +{ + return (x - hPlotLeft)*GetXRange()/(GetSize().cx - (hPlotLeft + hPlotRight) - 1) + GetXMin(); +} + +double ScatterDraw::GetYByPoint(const int y) +{ + return (GetSize().cy - vPlotTop - y - 1)*GetYRange()/(GetSize().cy - (vPlotTop + vPlotBottom) - GetTitleFont().GetHeight() - 1) + GetYMin(); +} + +double ScatterDraw::GetY2ByPoint(const int y) +{ + return (GetSize().cy - vPlotTop - y - 1)*GetY2Range()/(GetSize().cy - (vPlotTop + vPlotBottom) - GetTitleFont().GetHeight() - 1) + GetYMin2(); +} + +double ScatterDraw::GetXPointByValue(const double x) +{ + return (x - GetXMin())/GetXRange()*(GetSize().cx - (hPlotLeft + hPlotRight) - 1) + hPlotLeft; +} + +double ScatterDraw::GetYPointByValue(const double y) +{ + return (GetSize().cy - vPlotTop - 1) - (y - GetYMin())/GetYRange()*(GetSize().cy - (vPlotTop + vPlotBottom) - GetTitleFont().GetHeight() - 1); +} + +void ScatterDraw::Zoom(double scale, bool mouseX, bool mouseY) +{ + mouseX = mouseX && ((minXZoom > 0 && xRange*scale > minXZoom) || (minXZoom < 0)); + mouseX = mouseX && ((maxXZoom > 0 && xRange*scale < maxXZoom) || (maxXZoom < 0)); + mouseY = mouseY && ((minYZoom > 0 && yRange*scale > minYZoom) || (minYZoom < 0)); + mouseY = mouseY && ((maxYZoom > 0 && yRange*scale < maxYZoom) || (maxYZoom < 0)); + if (mouseX) { + if (zoomStyleX == TO_CENTER) { + double oldXMin = xMin; + xMin += xRange*(1-scale)/2.; + xMinUnit = oldXMin + xMinUnit - xMin; + AdjustMinUnitX(); + } + xRange *= scale; + if (!IsNull(maxMajorUnitsX)) { + if (xRange < xMajorUnit) + xMajorUnit /= 10; + else if (xRange/xMajorUnit > maxMajorUnitsX) + xMajorUnit *= 10; + } + } + if (mouseY) { + if (zoomStyleY == TO_CENTER) { + double oldYMin = yMin; + yMin += yRange*(1-scale)/2.; + yMinUnit = oldYMin + yMinUnit - yMin; + AdjustMinUnitY(); + double oldYMin2 = yMin2; + yMin2 += yRange2*(1-scale)/2.; + yMinUnit2 = oldYMin2 + yMinUnit2 - yMin2; + AdjustMinUnitY2(); + } + yRange *= scale; + yRange2 *= scale; + if (!IsNull(maxMajorUnitsY)) { + if (yRange < yMajorUnit) { + yMajorUnit /= 10; + yMajorUnit2 /= 10; + } else if (yRange/yMajorUnit > maxMajorUnitsY) { + yMajorUnit *= 10; + yMajorUnit2 *= 10; + } + } + } + if (mouseX || mouseY) { + WhenSetRange(); + Refresh(); + WhenZoomScroll(); + } +} + +void ScatterDraw::Scroll(double factorX, double factorY) +{ + if (factorX != 0) { + double deltaX = factorX*xRange; + xMin -= deltaX; + xMinUnit += deltaX; + AdjustMinUnitX(); + } + if (factorY != 0) { + double deltaY = -factorY*yRange; + yMin -= deltaY; + yMinUnit += deltaY; + AdjustMinUnitY(); + if (drawY2Reticle) { + double deltaY2 = -factorY*yRange2; + yMin2 -= deltaY2; + yMinUnit2 += deltaY2; + AdjustMinUnitY2(); + } + } + if (factorX != 0 || factorY != 0) { + Refresh(); + WhenZoomScroll(); + } +} + +Pointf PointAtLen(const Pointf &p0, const Pointf &p1, double len) +{ + Pointf ret; + if (p1.y == p0.y) { + ret.x = p0.x + ((p0.x < p1.x) ? len : -len); + ret.y = p0.y; + } else if (p1.x == p0.x) { + ret.x = p0.x; + ret.y = p0.y + ((p0.y < p1.y) ? len : -len); + } else { + double k = sqr((p1.x-p0.x)/(p1.y-p0.y)); + double deltax = len/sqrt(1/k + 1); + double deltay = len/sqrt(k + 1); + ret.x = p0.x + ((p0.x < p1.x) ? deltax : -deltax); + ret.y = p0.y + ((p0.y < p1.y) ? deltay : -deltay); + } + return ret; +} + +inline bool Even(int val) {return !(val%2);} + +Vector GetLineDash(String dash) +{ + Vector d; + CParser p(dash); + try { + while(!p.IsEof()) + if(!p.Char(':')) + d.Add(p.ReadDouble()); + } + catch(CParser::Error) {} + + if(d.GetCount() & 1) { + Vector dash1; + dash1.Append(d); + dash1.Append(d); + return dash1; + } + return d; +} + +Vector &GetDashedArray(String dash) +{ + static VectorMap > pats; + + int pos = pats.Find(dash); + if (pos < 0) { + pats.Add(dash, GetLineDash(dash)); + pos = pats.GetCount()-1; + } + return pats.GetValues()[pos]; +} + +void ScatterDraw::ParseTextMultiline(const String &text, Font fnt, Upp::Array &texts, Upp::Array &sizes) { + Size ret(0, 0); + int npos = 0; + for (int pos = 0; npos != -1; pos = npos+1) { + npos = text.Find('\n', pos); + String &t = texts.Add(); + if (npos != -1) + t = text.Mid(pos, npos-pos); + else + t = text.Mid(pos); + Size &s = sizes.Add(); + s.cx = GetTextSize(t, fnt).cx; + s.cy = GetTextSize(t, fnt).cy; + } +} + +Size GetTextSizeMultiline(Array &sizes) { + Size ret(0, 0); + for (int i = 0; i < sizes.GetCount(); ++i) { + if (sizes[i].cx > ret.cx) + ret.cx = sizes[i].cx; + ret.cy += sizes[i].cy; + } + return ret; +} + +ScatterDraw::ScatterDraw() +{ + mode = MD_ANTIALIASED; + size = Size(0, 0); + titleColor = SColorText(); + graphColor = White(); + titleFont = Roman(20); + labelsFont = StdFont(); + labelsColor = SColorText(); + plotAreaColor = SColorLtFace(); + axisColor = SColorText(); + axisWidth = 6; + hPlotLeft = hPlotRight = vPlotTop = vPlotBottom = 30; + xRange = yRange = yRange2 = 100.0; + xMin = yMin = yMin2 = xMinUnit = yMinUnit = yMinUnit2 = 0.0; + logX = logY = logY2 = false; + gridColor = SColorDkShadow(); + gridWidth = 1; + drawXReticle = true; drawYReticle = true; + drawY2Reticle = false; + drawVGrid = drawHGrid = showLegend = true; + legendWeight = 80; + minXZoom = maxXZoom = minYZoom = maxYZoom = -1; + fastViewX = false; + sequentialXAll = false; + zoomStyleX = zoomStyleY = TO_CENTER; + maxMajorUnitsX = maxMajorUnitsY = Null; + SetMajorUnitsNum(5, 10); + Color(graphColor); +} + +void DrawLine(Draw &w, double x0, double y0, double x1, double y1, double width, const Color &color) +{ + w.DrawLine(fround(x0), fround(y0), fround(x1), fround(y1), fround(width), color); +} + +void DrawLine(Painter &w, double x0, double y0, double x1, double y1, double width, const Color &color) +{ + w.Move(x0, y0).Line(x0, y0).Line(x1, y1).Stroke(width, color); +} + +void DrawVArrow(Draw &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color) +{ + DrawLine(w, x0, y0, x1, y1, width, color); + if (fabs(y0 - y1) > 2*aHeight) { + if (x0 > x1) + Swap(x0, x1); + if (y0 > y1) + Swap(y0, y1); + Vector arrowU, arrowL; + arrowU << Point(fround(x0), fround(y0)) << Point(fround(x0-aWidth), fround(y0+aHeight)) << Point(fround(x0+aWidth), fround(y0+aHeight)); + w.DrawPolygon(arrowU, SColorHighlight()); + arrowL << Point(fround(x0), fround(y1)) << Point(fround(x0-aWidth), fround(y1-aHeight)) << Point(fround(x0+aWidth), fround(y1-aHeight)); + w.DrawPolygon(arrowL, SColorHighlight()); + } +} + +void DrawVArrow(Painter &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color) +{ + DrawLine(w, x0, y0, x1, y1, width, color); + if (fabs(y0 - y1) > 2*aHeight) { + if (x0 > x1) + Swap(x0, x1); + if (y0 > y1) + Swap(y0, y1); + w.Move(x0, y0).Line(x0-aWidth, y0+aHeight).Line(x0+aWidth, y0+aHeight).Fill(SColorHighlight()); + w.Move(x0, y1).Line(x0-aWidth, y1-aHeight).Line(x0+aWidth, y1-aHeight).Fill(SColorHighlight()); + } +} + +void DrawHArrow(Draw &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color) +{ + DrawLine(w, x0, y0, x1, y1, width, color); + if (fabs(x0 - x1) > 2*aHeight) { + if (x0 > x1) + Swap(x0, x1); + if (y0 > y1) + Swap(y0, y1); + Vector arrowL, arrowR; + arrowL << Point(fround(x0), fround(y0)) << Point(fround(x0+aHeight), fround(y0+aWidth)) << Point(fround(x0+aHeight), fround(y0-aWidth)); + w.DrawPolygon(arrowL, SColorHighlight()); + arrowR << Point(fround(x1), fround(y0)) << Point(fround(x1-aHeight), fround(y0+aWidth)) << Point(fround(x1-aHeight), fround(y0-aWidth)); + w.DrawPolygon(arrowR, SColorHighlight()); + } +} + +void DrawHArrow(Painter &w, double x0, double y0, double x1, double y1, double width, double aWidth, double aHeight, const Color &color) +{ + DrawLine(w, x0, y0, x1, y1, width, color); + if (fabs(x0 - x1) > 2*aHeight) { + if (x0 > x1) + Swap(x0, x1); + if (y0 > y1) + Swap(y0, y1); + w.Move(x0, y0).Line(x0+aHeight, y0+aWidth).Line(x0+aHeight, y0-aWidth).Fill(SColorHighlight()); + w.Move(x1, y0).Line(x1-aHeight, y0+aWidth).Line(x1-aHeight, y0-aWidth).Fill(SColorHighlight()); + } +} + +void DrawText(Draw &w, double x, double y, int angle, const String &text, Font font, Color color) +{ + w.DrawText(fround(x), fround(y), angle, text, font, color); +} + +void DrawText(Painter &w, double x, double y, int angle, const String &text, Font font, Color color) +{ + w.Begin(); + w.Translate(x-0.5, y-0.5).Rotate(-angle*M_PI/1800.); + w.Text(0, 0, text, font).Fill(color); + w.End(); +} + +void Clip(Draw &w, double x, double y, double cx, double cy) +{ + w.Clip(fround(x), fround(y), fround(cx), fround(cy)); +} + +void Clip(Painter &w, double x, double y, double cx, double cy) +{ + w.Rectangle(x, y, cx, cy).Clip(); +} + +void ClipEnd(Draw &w) +{ + w.End(); +} + +void ClipEnd(Painter &w) +{ + ; +} + +void DrawLineOpa(Draw& w, const int x0, const int y0, const int x1, const int y1, const int &scale, + const double opacity, double thick, const Color &_color, String dash, + const Color &background) +{ + Vector p; + p << Point(x0, y0) << Point(x1, y1); + DrawPolylineOpa(w, p, scale, opacity, thick, _color, dash, background); +} + +void DashScaled(Painter& w, const String dash, double scale) +{ + if (!dash.IsEmpty()) { + Vector d; + double start = 0; + CParser p(dash); + try { + while(!p.IsEof()) + if(p.Char(':')) + start = p.ReadDouble(); + else + d.Add(scale*p.ReadDouble()); + } + catch(CParser::Error) {} + w.Dash(d, scale*start); + } +} + +void DrawLineOpa(Painter& w, const int x0, const int y0, const int x1, const int y1, const int &scale, + const double opacity, double thick, const Color &color, String dash, + const Color &background) +{ + w.Move(Pointf(x0, y0)); + w.Line(Pointf(x1, y1)); + DashScaled(w, dash, scale); + w.Opacity(opacity); // Before Stroke() + w.Stroke(thick*scale, color); +} + +void FillRectangleOpa(Draw &w, double x0, double y0, double x1, double y1, const int &scale, + const double opacity, const Color &background, const Color &color) +{ + if (IsNull(color)) + return; + Color opacolor = GetOpaqueColor(color, background, opacity); + if (x0 > x1) + Swap(x0, x1); + if (y0 > y1) + Swap(y0, y1); + w.DrawRect(int(x0), int(y0), abs(int(x1 - x0)), abs(int(y1 - y0)), opacolor); +} + +void FillRectangleOpa(Painter &w, double x0, double y0, double x1, double y1, const int &scale, + const double opacity, const Color &background, const Color &color) +{ + if (IsNull(color)) + return; + w.Rectangle(x0, y0, x1 - x0, y1 - y0).Opacity(opacity).Fill(color); +} + +void DrawPolylineOpa(Draw& w, const Vector &p, const int &scale, const double opacity, + double thick, const Color &_color, String dash, const Color &background) +{ + ASSERT(!p.IsEmpty()); + Color color = GetOpaqueColor(_color, background, opacity) ; + if (dash == LINE_SOLID) + w.DrawPolyline(p, fround(thick), color); + else { + Vector &pat = GetDashedArray(dash); + int iPat = 0; + + double len = pat[0]*scale; // Pixels per bar + Pointf begin, end; + begin = p[0]; + for (int i = 1; i < p.GetCount();) { + double d = Distance(begin, p[i]); + if (d >= len) + end = PointAtLen(begin, p[i], len); + else { + end = p[i]; + len -= d; + ++i; + } + if (Even(iPat)) + w.DrawLine(begin, end, fround(thick), color); + if (d >= len) { + iPat++; + if (iPat == pat.GetCount()) + iPat = 0; + len = pat[iPat]*scale; + } + begin = end; + } + } +} + +void DrawPolylineOpa(Painter& w, const Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String dash, const Color &background) +{ + ASSERT(!p.IsEmpty()); + w.Move(p[0]); + for (int i = 1; i < p.GetCount(); ++i) + w.Line(p[i]); + DashScaled(w, dash, scale); + w.Opacity(opacity); // Before Stroke() + w.Stroke(thick*scale, color); +} + +void FillPolylineOpa(Draw& w, const Vector &p, const int &scale, const double opacity, + const Color &background, const Color &fillColor) +{ + ASSERT(!p.IsEmpty()); + Color opacolor = GetOpaqueColor(fillColor, background, opacity) ; + + w.DrawPolygon(p, opacolor); +} + +void FillPolylineOpa(Painter& w, const Vector &p, const int &scale, const double opacity, + const Color &background, const Color &fillColor) +{ + ASSERT(!p.IsEmpty()); + w.Move(p[0]); + for (int i = 1; i < p.GetCount(); ++i) + w.Line(p[i]); + w.Opacity(opacity); + w.Fill(fillColor); // Before Stroke() +} + +INITBLOCK{ + SeriesPlot::Register("Line"); + SeriesPlot::Register("Staggered"); + SeriesPlot::Register("Bar"); + + MarkPlot::Register("Circle"); + MarkPlot::Register("Square"); + MarkPlot::Register("Triangle"); + MarkPlot::Register("Cross"); + MarkPlot::Register("X"); + MarkPlot::Register("Rhomb"); +} + +void GoBreakpoint() +{ + int kk = 1; +} \ No newline at end of file diff --git a/uppsrc/ScatterDraw/ScatterDraw.h b/uppsrc/ScatterDraw/ScatterDraw.h new file mode 100644 index 000000000..7e87857f3 --- /dev/null +++ b/uppsrc/ScatterDraw/ScatterDraw.h @@ -0,0 +1,649 @@ +#ifndef _ScatterDraw_ScatterDraw_h +#define _ScatterDraw_ScatterDraw_h + +#include +#include + +#include + +using namespace Upp; + +#include +#include "DrawingFunctions.h" +#include "SeriesPlot.h" +#include "MarkPlot.h" + + +Color GetOpaqueColor(const Color &color, const Color &background, const double opacity); + +class ScatterDraw { +public: + typedef ScatterDraw CLASSNAME; + ScatterDraw(); + + enum Formats { + EXP, + MON, + DY, + CUSTOM + }; + enum { + MD_DRAW = -1, + MD_ANTIALIASED = MODE_ANTIALIASED, + MD_NOAA = MODE_NOAA, + MD_SUBPIXEL = MODE_SUBPIXEL + }; + + #define LINE_DOTTED "4 10" + #define LINE_DOTTED_SEP "4 20" + #define LINE_DASHED "12 12" + #define LINE_DASH_DOT "20 10 5 10" + #define LINE_SOLID "" + +protected: + class ScatterBasicSeries { + public: + ScatterBasicSeries(); + void Init(int id); + + bool primaryY; + bool sequential; + + One seriesPlot; + double thickness; + Color color; + String dash; + + One markPlot; + double markWidth; + Color markColor; + + Color fillColor; + + String legend; + + double opacity; + + int id; + + bool trendLine; + //double trend + }; + + class ScatterSeries : public Moveable, public ScatterBasicSeries { + public: + ScatterSeries() {pD = 0;} + void SetDataSource(DataSource *pointsData, bool ownsData = true) {pD = pointsData; owns = ownsData;} + inline DataSource *PointsData() {return pD;} + ~ScatterSeries() {if(pD && owns) delete pD;} + private: + DataSource *pD; + bool owns; + }; + + static Color GetNewColor(int id); + static String GetNewDash(int id); + static MarkPlot *GetNewMarkPlot(int id); + +public: + Callback3 cbModifFormatX, cbModifFormatDeltaX; + Callback3 cbModifFormatY, cbModifFormatDeltaY; + Callback3 cbModifFormatY2, cbModifFormatDeltaY2; + + Callback WhenZoomScroll; + Callback WhenSetRange; + Callback WhenSetXYMin; + + ScatterDraw& SetSize(Size sz) {size = sz; return *this;}; + virtual Size GetSize() const {return size;}; + + ScatterDraw& SetColor(const Color& _color); + ScatterDraw& SetTitle(const String& _title); + ScatterDraw& SetTitleFont(const Font& fontTitle); + ScatterDraw& SetTitleColor(const Color& colorTitle); + + void SetLabels(const String& _xLabel, const String& _yLabel, const String& _yLabel2 = ""); + ScatterDraw& SetLabelX(const String& _xLabel); + ScatterDraw& SetLabelY(const String& _yLabel); + ScatterDraw& SetLabelY2(const String& _yLabel); + ScatterDraw& SetLabelsFont(const Font& fontLabels); + Font GetLabelsFont() {return labelsFont;}; + ScatterDraw& SetLabelsColor(const Color& colorLabels); + + ScatterDraw& SetPlotAreaMargin(const int hLeft, const int hRight, const int vTop, const int vBottom); + ScatterDraw& SetPlotAreaLeftMargin(const int margin); + ScatterDraw& SetPlotAreaRightMargin(const int margin); + ScatterDraw& SetPlotAreaTopMargin(const int margin); + ScatterDraw& SetPlotAreaBottomMargin(const int margin); + + ScatterDraw& SetPlotAreaColor(const Color& p_a_color); + + ScatterDraw& SetAxisColor(const Color& axis_color); + ScatterDraw& SetAxisWidth(const int& axis_width); + + ScatterDraw& SetGridColor(const Color& grid_color); + ScatterDraw& SetGridWidth(const int& grid_width); + ScatterDraw& ShowVGrid(const bool& show); + ScatterDraw& ShowHGrid(const bool& show); + + ScatterDraw& ShowLegend(const bool& show=true); + ScatterDraw& SetLegendWeight(const int& weight); + + ScatterDraw& SetMode(int _mode = MD_ANTIALIASED) {mode = _mode; Refresh(); return *this;}; + int GetMode() {return mode;}; + + void FitToData(bool Y = false); + void Zoom(double scale, bool hor = true, bool ver = true); + void Scroll(double factorX, double factorY); + + enum ZoomStyle {TO_CENTER, FROM_BASE}; + ScatterDraw &SetZoomStyleX(ZoomStyle style = TO_CENTER) {zoomStyleX = style; return *this;} + ScatterDraw &SetZoomStyleY(ZoomStyle style = TO_CENTER) {zoomStyleY = style; return *this;} + + ScatterDraw& SetRange(double rx, double ry, double ry2 = 100); + double GetXRange()const {return xRange;} + double GetYRange()const {return yRange;} + double GetY2Range()const {return yRange2;} + ScatterDraw &SetMajorUnits(double ux, double uy); + ScatterDraw &SetMajorUnitsNum(int nx, int ny); + ScatterDraw &SetMaxMajorUnitsX(int maxX, int maxY) {maxMajorUnitsX = maxX; maxMajorUnitsY = maxY; return *this;} + double GetMajorUnitsX() {return xMajorUnit;} + double GetMajorUnitsY() {return yMajorUnit;} + ScatterDraw& SetMinUnits(double ux, double uy); + double GetXMinUnit () const {return xMinUnit;} + double GetYMinUnit () const {return yMinUnit;} + + ScatterDraw& SetXYMin(double xmin,double ymin,double ymin2 = 0); + double GetXMin () const {return xMin;} + double GetYMin () const {return yMin;} + double GetYMin2 () const {return yMin2;} + + ScatterDraw &Graduation_FormatX(Formats fi); + ScatterDraw &Graduation_FormatY(Formats fi); + ScatterDraw &Graduation_FormatY2(Formats fi); + + ScatterDraw &AddSeries(double *yData, int numData, double x0 = 0, double deltaX = 1); + ScatterDraw &AddSeries(double *xData, double *yData, int numData); + ScatterDraw &AddSeries(Vector &xData, Vector &yData); + ScatterDraw &AddSeries(Upp::Array &xData, Upp::Array &yData); + ScatterDraw &AddSeries(Vector &points); + ScatterDraw &AddSeries(Upp::Array &points); + ScatterDraw &AddSeries(double (*function)(double)); + ScatterDraw &AddSeries(Pointf (*function)(double), int np, double from = 0, double to = 1); + ScatterDraw &AddSeries(PlotFunc &function); + ScatterDraw &AddSeries(PlotParamFunc function, int np, double from = 0, double to = 1); + ScatterDraw &_AddSeries(DataSource *data); + ScatterDraw &AddSeries(DataSource &data); + + template + ScatterDraw &AddSeries() {return _AddSeries(new C());} + template + ScatterDraw &AddSeries(T1 &arg1) + {return _AddSeries(new C(arg1));} + template + ScatterDraw &AddSeries(T1 &arg1, T2 &arg2) + {return _AddSeries(new C(arg1, arg2));} + template + ScatterDraw &AddSeries(T1 &arg1, T2 &arg2, T3 &arg3) + {return _AddSeries(new C(arg1, arg2, arg3));} + template + ScatterDraw &AddSeries(T1 &arg1, T2 &arg2, T3 &arg3, T4 &arg4) + {return _AddSeries(new C(arg1, arg2, arg3, arg4));} + template + ScatterDraw &AddSeries(T1 &arg1, T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5) + {return _AddSeries(new C(arg1, arg2, arg3, arg4, arg5));} + template + ScatterDraw &AddSeries(T1 &arg1, T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5, T6 &arg6) + {return _AddSeries(new C(arg1, arg2, arg3, arg4, arg5, arg6));} + + template + ScatterDraw &AddSeries(Vector &yData) {return _AddSeries(new VectorY(yData));} + template + ScatterDraw &AddSeries(Upp::Array &yData) {return _AddSeries(new ArrayY(yData));} + template + ScatterDraw &AddSeries(VectorMap &data) {return _AddSeries(new VectorMapXY(data));} + template + ScatterDraw &AddSeries(ArrayMap &data) {return _AddSeries(new ArrayMapXY(data));} + + void InsertSeries(int id, double *yData, int numData, double x0 = 0, double deltaX = 1); + void InsertSeries(int id, double *xData, double *yData, int numData); + void InsertSeries(int id, Vector &xData, Vector &yData); + void InsertSeries(int id, Upp::Array &xData, Upp::Array &yData); + void InsertSeries(int id, Vector &points); + void InsertSeries(int id, Upp::Array &points); + void InsertSeries(int id, double (*function)(double)); + void InsertSeries(int id, Pointf (*function)(double), int np, double from = 0, double to = 1); + void InsertSeries(int id, PlotFunc &function); + void InsertSeries(int id, PlotParamFunc function, int np, double from = 0, double to = 1); + void _InsertSeries(int id, DataSource *data); + + template + void InsertSeries(int id) {_InsertSeries(id, new C());} + template + void InsertSeries(int id, T1 &arg1) + {_InsertSeries(id, new C(arg1));} + template + void InsertSeries(int id, T1 &arg1, T2 &arg2) + {_InsertSeries(id, new C(arg1, arg2));} + template + void InsertSeries(int id, T1 &arg1, T2 &arg2, T3 &arg3) + {_InsertSeries(id, new C(arg1, arg2, arg3));} + template + void InsertSeries(int id, T1 &arg1, T2 &arg2, T3 &arg3, T4 &arg4) + {_InsertSeries(id, new C(arg1, arg2, arg3, arg4));} + template + void InsertSeries(int id, T1 &arg1, T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5) + {_InsertSeries(id, new C(arg1, arg2, arg3, arg4, arg5));} + template + void InsertSeries(int id, T1 &arg1, T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5, T6 &arg6) + {_InsertSeries(id, new C(arg1, arg2, arg3, arg4, arg5, arg6));} + + template + void InsertSeries(int id, Vector &yData) {_InsertSeries(id, new VectorY(yData));} + template + void InsertSeries(int id, Upp::Array &yData) {_InsertSeries(id, new ArrayY(yData));} + template + void InsertSeries(int id, VectorMap &data){_InsertSeries(id, new VectorMapXY(data));} + template + void InsertSeries(int id, ArrayMap &data) {_InsertSeries(id, new ArrayMapXY(data));} + + ScatterDraw &PlotStyle() {return PlotStyle(0);}; + template + ScatterDraw &PlotStyle() {return PlotStyle(new C());}; + template + ScatterDraw &PlotStyle(T1 &arg1) {return PlotStyle(new C(arg1));}; + template + ScatterDraw &PlotStyle(T1 &arg1, T2 &arg2) {return PlotStyle(new C(arg1, arg2));}; + template + ScatterDraw &PlotStyle(T1 &arg1, T2 &arg2, T3 &arg3) {return PlotStyle(new C(arg1, arg2, arg3));}; + ScatterDraw &PlotStyle(SeriesPlot *data); + + ScatterDraw &NoPlot() {return PlotStyle();}; + + ScatterDraw &MarkStyle() {return MarkStyle(0);}; + template + ScatterDraw &MarkStyle() {return MarkStyle(new C());}; + template + ScatterDraw &MarkStyle(T1 &arg1) {return MarkStyle(new C(arg1));}; + template + ScatterDraw &MarkStyle(T1 &arg1, T2 &arg2) {return MarkStyle(new C(arg1, arg2));}; + template + ScatterDraw &MarkStyle(T1 &arg1, T2 &arg2, T3 &arg3) {return MarkStyle(new C(arg1, arg2, arg3));}; + ScatterDraw &MarkStyle(MarkPlot *data); + + ScatterDraw &NoMark() {return MarkStyle();}; + + ScatterDraw &Stroke(double thickness = 3, Color color = Null); + ScatterDraw &Dash(const char *dash); + ScatterDraw &Fill(Color color = Null); + ScatterDraw &MarkColor(Color color = Null); + ScatterDraw &MarkWidth(const double& markWidth = 8); + ScatterDraw &Hide() {series[series.GetCount() - 1].opacity = 0; return *this;} + + ScatterDraw &Opacity(double opacity = 1) {series[series.GetCount() - 1].opacity = opacity; return *this;} + ScatterDraw &Legend(const String legend); + + inline bool IsValid(const int& j) const {return (j >= 0 && j < series.GetCount());} + + ScatterDraw& SetDrawXReticle(bool set = true); + ScatterDraw& SetDrawYReticle(bool set = true); + ScatterDraw& SetDrawY2Reticle(bool set = true); + + void SetDataColor(const int& j,const Color& pcolor); + Color GetDataColor (const int& j) const; + void SetDataThickness(const int& j, const double& thick); + double GetDataThickness(const int& j) const; + void SetFillColor(const int& j, const Color& color); + Color GetFillColor(const int& j) const; + + ScatterDraw &SetMarkWidth(const int& j, const double& width); + double GetMarkWidth(const int& j) const; + void SetMarkColor(const int& j, const Color& pcolor); + ::Color GetMarkColor (const int& j) const; + void NoMark(const int& j); + bool IsShowMark(const int& j) const throw (Exc); + + void SetDataPrimaryY(const int& j, const bool& primary=true); + ScatterDraw &SetDataPrimaryY(const bool& primary); + bool IsDataPrimaryY(const int& j) const throw (Exc); + + void SetSequentialX(const int& j, const bool& sequential = true); + ScatterDraw &SetSequentialX(const bool& sequential = true); + ScatterDraw &SetSequentialXAll(const bool& sequential = true); + + void Show(const int& j, const bool& show = true); + bool IsVisible(const int& j); + ScatterDraw &ShowAll(const bool& show = true); + + void RemoveSeries(const int& j); + void RemoveAllSeries(); + + ScatterDraw& Id(int id); + ScatterDraw& Id(const int& j, int id); + int GetId(const int& j); + + Drawing GetDrawing(); + Image GetImage(const int& scale = 1); + + #ifdef PLATFORM_WIN32 + void SaveAsMetafile(const char* file) const; + #endif + + ScatterDraw& LogX(const bool& logx=true) {logX=logx; return *this;} + ScatterDraw& LogY(const bool& logy=true) {logY=logy; return *this;} + ScatterDraw& LogY2(const bool& logy=true) {logY2=logy; return *this;} + + ScatterDraw& SetMinZoom(double x, double y = -1) {minXZoom = x; minYZoom = y; return *this;}; + ScatterDraw& SetMaxZoom(double x, double y = -1) {maxXZoom = x; maxYZoom = y; return *this;}; + + ScatterDraw& SetFastViewX(bool set = true) {fastViewX = set; return *this;}; + + Font& GetTitleFont() {return titleFont;}; + + double GetXByPoint(const int x); + double GetYByPoint(const int y); + double GetY2ByPoint(const int y); + double GetXPointByValue(const double x); + double GetYPointByValue(const double y); + + int GetCount() {return series.GetCount();} + bool IsEmpty() {return series.IsEmpty();} + + virtual void Refresh() {}; + +protected: + int mode; + class ::Color graphColor; + String title; + Font titleFont; + Color titleColor; + + String xLabel, yLabel, yLabel2; + Font labelsFont; + Color labelsColor; + + int hPlotLeft, hPlotRight, vPlotTop, vPlotBottom; + class ::Color plotAreaColor; + + bool fastViewX, sequentialXAll; + + Color axisColor; + int axisWidth; + + double xRange, yRange, yRange2; + double xMin, yMin, yMin2; + double xMajorUnit, yMajorUnit, yMajorUnit2; + double xMinUnit, yMinUnit, yMinUnit2; + double minXZoom, maxXZoom, minYZoom, maxYZoom; + bool drawXReticle, drawYReticle, drawY2Reticle; + + int maxMajorUnitsX, maxMajorUnitsY; + + Color gridColor; + int gridWidth; + bool drawVGrid, drawHGrid; + + int butDownX, butDownY; + bool isScrolling, isLabelPopUp; + ZoomStyle zoomStyleX, zoomStyleY; + + Vector series; + + bool showLegend; + int legendWeight; + + void DrawLegend(Draw& w,const int& scale) const; + + void Scrolling(bool down, Point &pt, bool isOut = false); + + void ExpFormat(String& s, int i, double d) {s = FormatDoubleExp(d,1);} + void MonFormat(String& s, int i, double d) {s = Format("%Mon",int(d));} + void DyFormat(String& s, int i, double d) {s = Format("%Dy",int(d));} + + static String VariableFormat(double range, double d) + { + if (fabs(d) <= 1e-15) + d = 0; + if (0.001<=range && range<0.01) return FormatDouble(d,5); + else if (0.01<=range && range<0.1) return FormatDouble(d,4); + else if (0.1<=range && range<1) return FormatDouble(d,3); + else if (1<=range && range<10) return FormatDouble(d,2); + else if (10<=range && range<100) return FormatDouble(d,1); + else if (100<=range && range<100000) return FormatDouble(d,0); + else return FormatDoubleExp(d,2); + + } + String VariableFormatX(const double& d) const {return VariableFormat(xRange, d);} + String VariableFormatY(const double& d) const {return VariableFormat(yRange, d);} + String VariableFormatY2(const double& d) const {return VariableFormat(yRange2, d);} + + template + void SetDrawing(T& w, const int& scale); + template + void Plot(T& w, const int& scale,const int& l,const int& h); + + void AdjustMinUnitX(); + void AdjustMinUnitY(); + void AdjustMinUnitY2(); + + bool logX, logY, logY2; + +private: + Size size; + static void ParseTextMultiline(const String &text, Font fnt, + Upp::Array &texts, Upp::Array &sizes); +}; + +void GoBreakpoint(); + +template +void ScatterDraw::SetDrawing(T& w, const int& scale) +{ + if (GetSize().cx == 0 || GetSize().cy == 0) + return; + + w.DrawRect(scale*GetSize(), graphColor); + + if (typeid(T) == typeid(BufferPainter)) { + Painter &p = (Painter &)w; + p.Translate(0, 0.5); + } + + Size sz(0, 0); + + int titleHeight = !title.IsEmpty() ? scale*titleFont.GetHeight() : 0; + + if(titleHeight > 0) { + Font fontTitle6; + fontTitle6 = titleFont; + fontTitle6.Height(titleHeight); + fontTitle6.Width(scale*titleFont.GetWidth()); + sz = GetTextSize(title, fontTitle6); + DrawText(w, (scale*GetSize().cx - sz.cx)/2., scale*2., 0, title, fontTitle6, titleColor); + } + + w.Offset(Point(scale*hPlotLeft, scale*vPlotTop + titleHeight)); + if(showLegend) + DrawLegend(w, scale); + + int plotW = scale*(GetSize().cx - (hPlotLeft + hPlotRight)); + int plotH = scale*(GetSize().cy - (vPlotTop + vPlotBottom)) - titleHeight; + + Font fontLabel; + fontLabel = labelsFont; + fontLabel.Height(scale*labelsFont.GetHeight()); + + Size lx = GetTextSize(xLabel, fontLabel); + Size ly = GetTextSize(yLabel, fontLabel); + Size ly2 = GetTextSize(yLabel2, fontLabel); + DrawText(w, (plotW - lx.cx)/2., scale*GetSize().cy - lx.cy - 70*scale, 0, xLabel, fontLabel, labelsColor); + DrawText(w, scale*2. - scale*hPlotLeft, (plotH + ly.cx)/2., 900, yLabel, fontLabel, labelsColor); + DrawText(w, scale*GetSize().cx - ly2.cy - 42*scale, (plotH + ly2.cx)/2., 900, yLabel2, fontLabel, labelsColor); + + if (typeid(T) == typeid(BufferPainter)) { + Painter &p = (Painter &)w; + p.Translate(0.5, 0); + } +GoBreakpoint(); + if (drawXReticle) + for(int i = 0; xMinUnit + i*xMajorUnit <= xRange; i++){ + w.DrawLine(fround(plotW*xMinUnit/xRange + i*plotW/(xRange/xMajorUnit)), plotH, + fround(plotW*xMinUnit/xRange + i*plotW/(xRange/xMajorUnit)), plotH + scale*4, + fround(gridWidth*scale), axisColor); + Font standard6; + standard6.Height(scale*StdFont().GetHeight()); + double gridX = xMinUnit + i*xMajorUnit + xMin; + String gridLabelX; + if (cbModifFormatX) + cbModifFormatX(gridLabelX, i, gridX); + else + gridLabelX = VariableFormatX(gridX); + + Array texts; + Array sizes; + ParseTextMultiline(gridLabelX, StdFont(), texts, sizes); + for (int ii = 0; ii < texts.GetCount(); ++ii) { + int cy = ii == 0 ? 0 : sizes[ii - 1].cy; + DrawText(w, plotW*xMinUnit/xRange + i*plotW/(xRange/xMajorUnit) - scale*sizes[ii].cx/2., + plotH + scale*(4 + ii*cy), 0, texts[ii], standard6, axisColor); + } + } +GoBreakpoint(); + if (drawYReticle) + for(int i = 0; yMinUnit + i*yMajorUnit <= yRange; i++){ + w.DrawLine(-(scale*4), fround(-plotH*yMinUnit/yRange + plotH - i*plotH/(yRange/yMajorUnit)), + 0, fround(-plotH*yMinUnit/yRange + plotH - i*plotH/(yRange/yMajorUnit)), + fround(gridWidth*scale), axisColor); + double gridY = yMinUnit + i*yMajorUnit + yMin; + String gridLabelY; + if (cbModifFormatY) + cbModifFormatY(gridLabelY, i, gridY); + else + gridLabelY = VariableFormatY(gridY); + int dx=scale*GetTextSize(gridLabelY,StdFont()).cx; + Font Standard6; + Standard6.Height(scale*StdFont().GetHeight()); + DrawText(w, -dx - scale*6, fround(-plotH*yMinUnit/yRange + plotH - i*plotH/(yRange/yMajorUnit)) - scale*8, + 0, gridLabelY, Standard6, axisColor); + } +GoBreakpoint(); + if (drawY2Reticle) + for(int i = 0; yMinUnit + i*yMajorUnit <= yRange; i++){ + w.DrawLine(plotW + (scale*4), fround(-plotH*yMinUnit2/yRange2 + plotH - i*plotH/(yRange/yMajorUnit)), + plotW, fround(-plotH*yMinUnit2/yRange2 + plotH - i*plotH/(yRange/yMajorUnit)), + fround(gridWidth*scale), axisColor); + double gridY2 = yMinUnit2 + i*yMajorUnit2 + yMin2; + String gridLabelY2; + if (cbModifFormatY2) + cbModifFormatY2(gridLabelY2, i, gridY2); + else + gridLabelY2 = VariableFormatY2(gridY2); + Font standard6; + standard6.Height(scale*StdFont().GetHeight()); + DrawText(w, plotW + scale*10, fround(-plotH*yMinUnit2/yRange2 + plotH - i*plotH/(yRange/yMajorUnit)) - scale*8, 0, + gridLabelY2, standard6, axisColor); + } +GoBreakpoint(); + Plot(w, scale, plotW, plotH); + ClipEnd(w); +} + +template +void ScatterDraw::Plot(T& w, const int& scale, const int& plotW, const int& plotH) +{ + double d1 = xRange/xMajorUnit; + double d2 = yRange/yMajorUnit; + + w.DrawRect(1, 1, plotW - 2, plotH - 1, plotAreaColor); + + if (drawVGrid) + for(int i = 0; xMinUnit + i*xMajorUnit < xRange; i++) + DrawLineOpa(w, fround(plotW*xMinUnit/xRange + i*plotW/d1), 0, fround(plotW*xMinUnit/xRange + i*plotW/d1), plotH, + 1, 1, gridWidth, gridColor, "2 2"); + + if (drawHGrid) + for(int i = 0; yMinUnit + i*yMajorUnit < yRange; i++) + DrawLineOpa(w, 0, fround(-plotH*yMinUnit/yRange + plotH - i*plotH/d2), plotW, fround(-plotH*yMinUnit/yRange + plotH - i*plotH/d2), + 1, 1, gridWidth, gridColor, "2 2"); + + w.DrawLine(0, plotH, plotW, plotH, fround(gridWidth*scale), Black); + w.DrawLine(0, 0, plotW, 0, fround(gridWidth*scale), Black); + w.DrawLine(0, 0, 0, plotH, fround(gridWidth*scale), Black); + w.DrawLine(plotW, 0, plotW, plotH + 1, fround(gridWidth*scale), Black); + + Clip(w, 0, 0, plotW, plotH); +GoBreakpoint(); + if (!series.IsEmpty()) { + for (int j = 0; j < series.GetCount(); j++) { + if (series[j].opacity == 0 || (!series[j].seriesPlot && !series[j].markPlot)) + continue; + Vector p1; + int imin, imax; + if (series[j].sequential) { + imin = imax = Null; + for (int i = 1; i < series[j].PointsData()->GetCount() - 1; ++i) { + if (IsNull(imin)) { + if (series[j].PointsData()->x(i) >= xMin) + imin = i - 1; + } else if (IsNull(imax)) { + if (series[j].PointsData()->x(i) >= xMin + xRange) + imax = i + 1; + } + } + if (IsNull(imin)) + imin = 0; + if (IsNull(imax)) + imax = series[j].PointsData()->GetCount(); + } else if (series[j].PointsData()->IsParam()) { // It is a param function + imin = 0; + imax = series[j].PointsData()->GetCount(); + } else if (IsNull(series[j].PointsData()->GetCount())) { // It is a function + imin = fround(xMin) - 1; + imax = fround(xMin + xRange) + 1; + } else { + imin = 0; + imax = series[j].PointsData()->GetCount(); + } + int numV; + if (fastViewX) + numV = 1 + (imax - imin)/plotW; + else + numV = 1; + for (int i = imin; i < imax; i += numV) { + double xx, yy; + if (fastViewX && numV > 1) { + yy = 0; + int ii; + for (ii = 0; ii < numV && i + ii < imax; ++ii) + yy += series[j].PointsData()->y(i + ii); + yy /= double(ii); + xx = (series[j].PointsData()->x(i) + series[j].PointsData()->x(i + ii - 1))/2; + } else { + xx = series[j].PointsData()->x(i); + yy = series[j].PointsData()->y(i); + } + int ix, iy; + ix = fround(plotW*(xx - xMin)/xRange); + if (series[j].primaryY) + iy = fround(plotH*(yy - yMin)/yRange); + else + iy = fround(plotH*(yy - yMin2)/yRange2); + p1 << Point(ix, plotH - iy); + } + if (!p1.IsEmpty()) + series[j].seriesPlot->Paint(w, p1, scale, series[j].opacity, + fround(series[j].thickness), series[j].color, + series[j].dash, plotAreaColor, series[j].fillColor, plotW/xRange, plotH/yRange, + int(plotH*(1 + yMin/yRange))); + + if (series[j].markWidth >= 1 && series[j].markPlot) { + for (int i = 0; i < (imax - imin)/numV; i++) + series[j].markPlot->Paint(w, scale, p1[i], series[j].markWidth, series[j].markColor); + } + } + } + w.End(); +} + +#endif + diff --git a/uppsrc/ScatterDraw/ScatterDraw.t b/uppsrc/ScatterDraw/ScatterDraw.t new file mode 100644 index 000000000..d56eaba2e --- /dev/null +++ b/uppsrc/ScatterDraw/ScatterDraw.t @@ -0,0 +1,3 @@ +#ifdef _MSC_VER +#pragma setlocale("C") +#endif \ No newline at end of file diff --git a/uppsrc/ScatterDraw/ScatterDraw.upp b/uppsrc/ScatterDraw/ScatterDraw.upp new file mode 100644 index 000000000..4feba6f71 --- /dev/null +++ b/uppsrc/ScatterDraw/ScatterDraw.upp @@ -0,0 +1,14 @@ +description "Scatter control\377"; + +uses + Painter, + DataSource; + +file + ScatterDraw.h, + DrawingFunctions.h, + SeriesPlot.h, + MarkPlot.h, + ScatterDraw.cpp, + ScatterDraw.t; + diff --git a/uppsrc/ScatterDraw/SeriesPlot.h b/uppsrc/ScatterDraw/SeriesPlot.h new file mode 100644 index 000000000..4c1089d95 --- /dev/null +++ b/uppsrc/ScatterDraw/SeriesPlot.h @@ -0,0 +1,156 @@ +#ifndef _ScatterDraw_SeriesPlot_h_ +#define _ScatterDraw_SeriesPlot_h_ + +using namespace Upp; + +class SeriesPlot { +public: + virtual ~SeriesPlot() {}; + virtual void Paint(Draw& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const {}; + virtual void Paint(Painter& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const {}; + template + static void Register(const String& name) + { + classMap().FindAdd(name, __Create); + typeMap().FindAdd(typeid(T).name(), name); + } + static void Unregister(const String& name) + { + int i = TypeIndex(name); + ASSERT(i >= 0); + classMap().Remove(i); + typeMap().Remove(i); + } + static String TypeName(int i) {return typeMap()[i];} + static int TypeIndex(const String& name) {return max(classMap().Find(name),0);} + static int GetCount() {return classMap().GetCount();} + static SeriesPlot* Create(int i) {return classMap()[i]();} + static Vector GetTypes() {return VectorMap(typeMap(),1).GetValues();} + int GetType()const {return typeMap().Find(typeid(*this).name());} + SeriesPlot* Copy()const; + +protected: + typedef SeriesPlot* (*CreateFunc)(); + template + static SeriesPlot* __Create() {return new T;} + static VectorMap& classMap() {static VectorMap cMap; return cMap;} + static VectorMap& typeMap() {static VectorMap tMap; return tMap;} +}; + +class LineSeriesPlot : public SeriesPlot { +private: + template + void DoPaint(T& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, int y0) const + { + if (!IsNull(fillColor)) { + int x = p[0].x; + p.Insert(0, Point(x, y0)); + x = p[p.GetCount() - 1].x; + p.Add(Point(x, y0)); + FillPolylineOpa(w, p, scale, opacity, background, fillColor); + p.Remove(0); + p.Remove(p.GetCount() - 1); + } + DrawPolylineOpa(w, p, scale, 1, thick, color, pattern, background); + } + +public: + void Paint(Draw& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const + { + DoPaint(w, p, scale, opacity, fround(thick), color, pattern, background, fillColor, y0); + } + void Paint(Painter& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const + { + DoPaint(w, p, scale, opacity, fround(thick), color, pattern, background, fillColor, y0); + } +}; + + +class StaggeredSeriesPlot : public SeriesPlot { +private: + template + void DoPaint(T& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, int y0) const + { + ASSERT(p.GetCount() > 1); + Vector ps; + ps << Pointf(p[0].x - (p[1].x - p[0].x)/2., p[0].y); + for (int i = 1; i < p.GetCount(); ++i) { + double x = (p[i].x + p[i-1].x)/2.; + ps << Pointf(x, p[i-1].y); + ps << Pointf(x, p[i].y); + } + ps << Pointf(p[p.GetCount() - 1].x + (p[p.GetCount() - 1].x - p[p.GetCount() - 2].x)/2., p[p.GetCount() - 1].y); + if (!IsNull(fillColor)) { + int x = ps[0].x; + ps.Insert(0, Point(x, y0)); + x = ps[ps.GetCount() - 1].x; + ps.Add(Point(x, y0)); + FillPolylineOpa(w, ps, scale, opacity, background, fillColor); + ps.Remove(0); + ps.Remove(ps.GetCount() - 1); + } + DrawPolylineOpa(w, ps, scale, 1, fround(thick), color, pattern, background); + } + +public: + void Paint(Draw& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const + { + DoPaint(w, p, scale, opacity, thick, color, pattern, background, fillColor, y0); + } + void Paint(Painter& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const + { + DoPaint(w, p, scale, opacity, thick, color, pattern, background, fillColor, y0); + } +}; + +class BarSeriesPlot : public SeriesPlot { +private: + template + void DoPaint(T& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, int y0) const + { + for (int i = 0; i < p.GetCount(); ++i) { + FillRectangleOpa(w, p[i].x - width*fx, y0, p[i].x + width*fx, p[i].y, scale, opacity, background, fillColor); + Vector ps; + ps << Pointf(fround(p[i].x - width*fx), y0) << Pointf(fround(p[i].x - width*fx), p[i].y) + << Pointf(fround(p[i].x + width*fx), p[i].y) << Pointf(fround(p[i].x + width*fx), y0); + DrawPolylineOpa(w, ps, scale, 1, fround(thick), color, pattern, background); + } + } + double width; + +public: + BarSeriesPlot(double width) : width(width) {}; + BarSeriesPlot() {}; + void Paint(Draw& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const + { + DoPaint(w, p, scale, opacity, thick, color, pattern, background, fillColor, fx, y0); + } + void Paint(Painter& w, Vector &p, const int &scale, const double opacity, + double thick, const Color &color, String pattern, const Color &background, + const Color &fillColor, double fx, double fy, int y0) const + { + DoPaint(w, p, scale, opacity, thick, color, pattern, background, fillColor, fx, y0); + } +}; + +#endif diff --git a/uppsrc/ScatterDraw/icon.ico b/uppsrc/ScatterDraw/icon.ico new file mode 100644 index 000000000..4012786aa Binary files /dev/null and b/uppsrc/ScatterDraw/icon.ico differ diff --git a/uppsrc/ScatterDraw/init b/uppsrc/ScatterDraw/init new file mode 100644 index 000000000..025e87d02 --- /dev/null +++ b/uppsrc/ScatterDraw/init @@ -0,0 +1,5 @@ +#ifndef _ScatterDraw_icpp_init_stub +#define _ScatterDraw_icpp_init_stub +#include "Painter/init" +#include "DataSource/init" +#endif