ultimatepp/bazaar/Scatter/Scatter.cpp
koldo d2c1bb3548 Scatter: Added GetNewPattern and fixed IsGui() problem detected by Didier
git-svn-id: svn://ultimatepp.org/upp/trunk@4340 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2011-12-22 22:27:26 +00:00

1962 lines
51 KiB
C++

#include "Scatter.h"
#define IMAGECLASS ChartImg
#define IMAGEFILE <Scatter/Chart.iml>
#include <Draw/iml.h>
Scatter::MouseBehaviour defaultMouse[] = {
{false, false, false, true , false, 0, false, Scatter::SHOW_INFO},
{false, false, false, false, false, 0, true , Scatter::CONTEXT_MENU},
{false, false, false, false, true , 0, false, Scatter::SCROLL},
{false, false, false, false, false, 1, false, Scatter::ZOOM_H_RED},
{false, false, false, false, false, 1, false, Scatter::ZOOM_V_RED},
{false, false, false, false, false,-1, false, Scatter::ZOOM_H_ENL},
{false, false, false, false, false,-1, false, Scatter::ZOOM_V_ENL},
{false, false, false, false, false, 0, false, Scatter::NO_ACTION}};
Scatter& Scatter::SetColor(const class::Color& _color)
{
graphColor=_color;
return *this;
}
Scatter& Scatter::SetTitle(const String& _title)
{
title=_title;
return *this;
}
Scatter& Scatter::SetTitleFont(const Font& fontTitle)
{
titleFont=fontTitle;
return *this;
}
Scatter& Scatter::SetTitleColor(const class::Color& colorTitle)
{
titleColor=colorTitle;
return *this;
}
void Scatter::SetLabels(const WString& _xLabel, const WString& _yLabel, const WString& _yLabel2)
{
xLabel=_xLabel;
yLabel=_yLabel;
yLabel2=_yLabel2;
}
Scatter& Scatter::SetLabelX(const WString& _xLabel)
{
xLabel=_xLabel;
return *this;
}
Scatter& Scatter::SetLabelY(const WString& _yLabel)
{
yLabel=_yLabel;
return *this;
}
Scatter& Scatter::SetLabelY2(const WString& _yLabel)
{
yLabel2=_yLabel;
return *this;
}
Scatter& Scatter::SetLabelsFont(const Font& fontLabels)
{
labelsFont=fontLabels;
return *this;
}
Scatter& Scatter::SetLabelsColor(const class::Color& colorLabels)
{
labelsColor=colorLabels;
return *this;
}
Scatter& Scatter::SetPlotAreaPoz(const int& poz_x, const int& poz_y)
{
px=poz_x;
py=poz_y;
return *this;
}
Scatter& Scatter::H_Border(const int& poz_x)
{
if(poz_x>=0)
px=poz_x;
return *this;
}
Scatter& Scatter::V_Border(const int& poz_y)
{
if(poz_y>=0)
py=poz_y;
return *this;
}
Scatter& Scatter::SetPlotAreaColor(const class::Color& p_a_color)
{
plotAreaColor=p_a_color;
return *this;
}
Scatter& Scatter::SetAxisColor(const class::Color& axis_color)
{
axisColor=axis_color;
return *this;
}
Scatter& Scatter::SetAxisWidth(const int& axis_width)
{
axisWidth=axis_width;
return *this;
}
Scatter& Scatter::SetGridColor(const class::Color& grid_color)
{
gridColor=grid_color;
return *this;
}
Scatter& Scatter::SetGridWidth(const int& grid_width)
{
gridWidth=grid_width;
return *this;
}
Scatter& Scatter::ShowHGrid(const bool& show)
{
drawHGrid=show;
return *this;
}
Scatter& Scatter::ShowVGrid(const bool& show)
{
drawVGrid=show;
return *this;
}
Scatter& Scatter::ShowLegend(const bool& show)
{
showLegend=show;
return *this;
}
Scatter& Scatter::SetLegendWeight(const int& weight)
{
legendWeight=weight;
return *this;
}
Scatter& Scatter::SetAntialiasing(const bool& aa)
{
antialiasing=aa;
return *this;
}
Scatter &Scatter::SetDrawXReticle(bool set)
{
drawXReticle = set;
return *this;
}
Scatter &Scatter::SetDrawYReticle(bool set)
{
drawYReticle = set;
return *this;
}
Scatter &Scatter::SetDrawY2Reticle(bool set)
{
drawY2Reticle = set;
return *this;
}
void Scatter::DrawLegend(Draw& w,const int& scale) const
{
Vector<String> L;
L.Append(vLegend);
L.Append(vFLegend);
Vector<int> Thick;
Thick.Append(vPThickness);
Thick.Append(vFThickness);
Vector<class::Color> LC;
LC.Append(vPColors);
LC.Append(vFColors);
Vector<int> LW;
LW.Append(vPWidth);
Vector<bool>VJ;
VJ.Append(vJoin);
Vector<int> MS;
MS.Append(vMarkStyles);
Vector<class::Color> MC;
MC.Append(vMarkColors);
Vector<String> Patt;
Patt.Append(vPPattern);
Patt.Append(vFPattern);
int NMR=fround((GetSize().cx-2*px)/legendWeight);//max number of labels per row
if (NMR>0)
{
int N=L.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>N)
{Nc=0; LCR=0; R=N;}
else if (NMR==N)
{Nc=1; LCR=N; R=0;}
else
{Nc=N/NMR; LCR=NMR; R=N%NMR;}
for(int j=0;j<=Nc;j++)
{
int start=N-(j+1)*LCR;
int end=N-j*LCR;
if (j==Nc)
{start=0; end=R;}
for(int i=start;i<end;i++)
{
/*w.DrawRect(scale*(i-start)*legendWeight,
scale*(4-12*(j+1)),
14*scale,
scale*Thick.At(i)/6,
LC.At(i));*/
Vector <Point> 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 (VJ.At(i))
DrawPolylineX(w, vp, scale*Thick.At(i)/6, LC.At(i), Patt.At(i), scale);
Point p(scale*((i-start)*legendWeight+7),scale*(4-12*(j+1))/*+scale*Thick.At(i)/12*/);
if (LW.At(i,0) > 1)
DrawMark(MS.At(i),w,scale,p,LW.At(i,0),MC.At(i));
Font scaledFont;
scaledFont.Height(scale*StdFont().GetHeight());
w.DrawText(scale*(i-start)*legendWeight+scale*25,
scale*(-2-12*(j+1)),
L.At(i),
scaledFont,LC.At(i));
}
}
}
}
void Scatter::AdjustMinUnitX()
{
if (xMajorUnit > 0) {
while (xMinUnit < 0)
xMinUnit += xMajorUnit;
while (xMinUnit > xMajorUnit)
xMinUnit -= xMajorUnit;
}
}
void Scatter::AdjustMinUnitY()
{
if (yMajorUnit > 0) {
while (yMinUnit < 0)
yMinUnit += yMajorUnit;
while (yMinUnit > yMajorUnit)
yMinUnit -= yMajorUnit;
}
}
void Scatter::AdjustMinUnitY2()
{
if (yMajorUnit2 > 0) {
while (yMinUnit2 < 0)
yMinUnit2 += yMajorUnit2;
while (yMinUnit2 > yMajorUnit2)
yMinUnit2 -= yMajorUnit2;
}
}
Scatter &Scatter::SetRange(double rx, double ry, double ry2)
{
if (rx <= 0 || ry <= 0 || ry2 <= 0)
throw (Exc(t_("Invalid graph range")));
xRange=rx;
yRange=ry;
yRange2=ry2;
xMajorUnit=xRange/10;
AdjustMinUnitX();
yMajorUnit=yRange/10;
AdjustMinUnitY();
yMajorUnit2=yRange2/10;
AdjustMinUnitY2();
WhenSetRange();
return *this;
}
Scatter &Scatter::SetMajorUnits(double ux, double uy)
{
//if (ux > xRange) throw (Exc(t_("Invalid X major units!")));
//if (uy > yRange) throw (Exc(t_("Invalid Y major units!")));
xMajorUnit=ux;
yMajorUnit=uy;
yMajorUnit2=yRange2*yMajorUnit/yRange;
AdjustMinUnitX();
AdjustMinUnitY();
AdjustMinUnitY2();
return *this;
}
Scatter &Scatter::SetMinUnits(double ux, double uy)
{
xMinUnit=ux;
yMinUnit=uy;
yMinUnit2=yRange2*yMinUnit/yRange;
AdjustMinUnitX();
AdjustMinUnitY();
AdjustMinUnitY2();
return *this;
}
Scatter &Scatter::SetXYMin(double xmin, double ymin, double ymin2)
{
xMin=xmin;
yMin=ymin;
yMin2=ymin2;
WhenSetXYMin();
return *this;
}
void Scatter::FitToData(bool Y) {
double minx, maxx, miny, miny2, maxy, maxy2;
minx = miny = miny2 = -DOUBLE_NULL;
maxx = maxy = maxy2 = DOUBLE_NULL;
for (int j=0; j<vPointsData.GetCount(); j++) {
for (int i=0; i<vPointsData[j].GetCount(); i++) {
if (vPointsData[j][i].x < minx)
minx = vPointsData[j][i].x;
if (vPointsData[j][i].x > maxx)
maxx = vPointsData[j][i].x;
}
}
for (int j=0; j<vFunctionData.GetCount(); j++) {
if(!vFunctionData[j].IsEmpty()) {
for (int i=0; i<vFunctionData[j].GetCount(); i++) {
if (vFunctionData[j][i].x < minx)
minx = vFunctionData[j][i].x;
if (vFunctionData[j][i].x < maxx)
maxx = vFunctionData[j][i].x;
}
}
}
if (Y) {
for (int j=0; j<vPointsData.GetCount(); j++) {
for (int i=0; i<vPointsData[j].GetCount(); i++) {
if (vPPrimaryY[j]) {
if (vPointsData[j][i].y < miny)
miny = vPointsData[j][i].y;
if (vPointsData[j][i].y > maxy)
maxy = vPointsData[j][i].y;
} else {
if (vPointsData[j][i].y < miny2)
miny2 = vPointsData[j][i].y;
if (vPointsData[j][i].y > maxy2)
maxy2 = vPointsData[j][i].y;
}
}
}
for (int j=0; j<vFunctionData.GetCount(); j++) {
if(!vFunctionData[j].IsEmpty()) {
for (int i=0; i<vFunctionData[j].GetCount(); i++) {
if (vFPrimaryY[j]) {
if (vFunctionData[j][i].y < miny)
miny = vFunctionData[j][i].y;
if (vFunctionData[j][i].y < maxy)
maxy = vFunctionData[j][i].y;
} else {
if (vFunctionData[j][i].y < miny2)
miny2 = vFunctionData[j][i].y;
if (vFunctionData[j][i].y < maxy2)
maxy2 = vFunctionData[j][i].y;
}
}
}
}
}
double deltaX = xMin - minx;
xMin -= deltaX;
xMinUnit += deltaX;
AdjustMinUnitX();
xRange = maxx - minx;
if (Y) {
miny = min((miny-yMin)/yRange, (miny2-yMin2)/yRange2);
maxy = max((maxy-yMin-yRange)/yRange, (maxy2-yMin2-yRange2)/yRange2);
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();
}
void Scatter::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;
}
}
void Scatter::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;
}
}
void Scatter::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;
}
}
Scatter &Scatter::AddSeries(Vector<XY> & points,const String& legend,const bool& join, const class::Color& pcolor, const int& width, const int& thickness)
{
vPointsData.AddPick(points);
vJoin.Add(join);
vSmooth.Add(false);
vPColors.Add(pcolor);
vPWidth.Add(width);
vPThickness.Add(thickness);
vLegend.Add(legend);
vMarkColors.Add(pcolor);
if (width <= 1)
vShowMark.Add(false);
else
vShowMark.Add(true);
vMarkStyles.Add(CIRCLE);
vPPrimaryY.Add(true);
vPSequential.Add(sequentialXAll);
vPPattern.Add(LINE_SOLID);
Refresh();
return *this;
}
Color 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 Yellow();
case 6: return Brown();
case 7: return Blue();
case 8: return Red();
case 9: return Green();
case 10:return Gray();
}
return Color(Random(), Random(), Random());
}
#define LINE_DOTTED "o..."
#define LINE_DOTTED_SEP "o..........."
#define LINE_DASHED "oooooo......"
#define LINE_DASH_DOT "ooooo...o..."
#define LINE_SOLID "oooooooooooo"
String GetNewPattern(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;
}
String ret = "oo";
dword r = Random();
return ret << (r & 1 ? "oo" : "..") << (r & 2 ? "oo" : "..") << (r & 4 ? "oo" : "..");
}
Scatter &Scatter::Stroke(int width, class::Color pcolor, String pattern)
{
int id = vPointsData.GetCount() - 1;
vJoin[id] = width > 0;
if (IsNull(pcolor))
pcolor = GetNewColor(id);
vPColors[id] = pcolor;
vPThickness[id] = width;
if (IsNull(pattern))
pattern = GetNewPattern(id);
vPPattern[id] = pattern;
Refresh();
return *this;
}
Scatter &Scatter::Mark(int thickness, class::Color pcolor, MarkStyle style)
{
int id = vPointsData.GetCount() - 1;
vPWidth[id] = thickness;
if (IsNull(pcolor))
pcolor = GetNewColor(id);
vMarkColors[id] = pcolor;
vShowMark[id] = thickness > 0;
vMarkStyles[id] = style;
Refresh();
return *this;
}
Scatter &Scatter::SetPattern(const String pattern)
{
vPPattern[vPPattern.GetCount()-1] = pattern;
return *this;
}
void Scatter::AddPoint(const int& j, const XY & point, const bool& refresh)
{
if(IsValid(j))
{
vPointsData[j].Add(point);
if(refresh) Refresh();
}
}
void Scatter::InsertPoint(const int& j, const int& i, const XY & point)
{
if(IsValid(j))
{
if(i>=0 && vPointsData[j].GetCount()>=i)
{
vPointsData[j].Insert(i,point);
Refresh();
}
}
}
void Scatter::RemovePoint(const int& j, const int& i)
{
if(IsValid(j))
{
if(i>=0 && vPointsData[j].GetCount()>i)
{
vPointsData[j].Remove(i);
Refresh();
}
}
}
void Scatter::SetData(const int& j, const int& i, const XY & point)
{
if(IsValid(j))
{
if(i>=0 && vPointsData[j].GetCount()>i)
{
vPointsData[j][i]=point;
Refresh();
}
}
}
void Scatter::SetData(const int& j, Vector<XY> & points)
{
if(IsValid(j))
{
vPointsData[j]<<=points;
Refresh();
}
}
void Scatter::SetDataColor(const int& j, const class::Color& pcolor)
{
if(IsValid(j))
{
vPColors[j]=pcolor;
Refresh();
}
}
void Scatter::SetFunctColor(const int& j, const class::Color& fcolor)
{
if(IsValid(j))
{
vFColors[j]=fcolor;
Refresh();
}
}
Color Scatter::GetDataColor(const int& j) const
{
if(IsValid(j))
{
return vPColors[j];
}
throw (Exc(t_("Invalid series index!")));
}
Color Scatter::GetFunctColor(const int& j) const
{
if(IsValid(j))
{
return vFColors[j];
}
throw (Exc(t_("Invalid function index!")));
}
void Scatter::SetDataThickness(const int& j, const int& thick_dots)
{
if(IsValid(j))
{
vPThickness[j]=thick_dots;
Refresh();
}
}
void Scatter::SetFunctThickness(const int& j, const int& thick_dots)
{
if(IsValid(j))
{
vFThickness[j]=thick_dots;
Refresh();
}
}
int Scatter::GetDataThickness(const int& j) const
{
if(IsValid(j))
{
return vPThickness[j];
}
throw (Exc(t_("Invalid series index!")));
}
int Scatter::GetFunctThickness(const int& j) const
{
if(IsValid(j))
{
return vFThickness[j];
}
throw (Exc(t_("Invalid series index!")));
}
void Scatter::SetMarkWidth(const int& j, const int& width_dots)
{
if(IsValid(j))
{
vPWidth[j]=width_dots;
Refresh();
}
}
int Scatter::GetMarkWidth(const int& j) const
{
if(IsValid(j))
{
return vPWidth[j];
}
throw (Exc(t_("Invalid series index!")));
}
void Scatter::SetMarkStyle(const int& j, MarkStyle noStyle)
{
if(IsValid(j))
{
vMarkStyles[j]=noStyle;
Refresh();
}
}
int Scatter::GetMarkStyle(const int& j) const
{
if(IsValid(j))
{
return vMarkStyles[j];
}
throw (Exc(t_("Invalid series index!")));
}
void Scatter::SetMarkColor(const int& j, const ::Color& mcolor)
{
if(IsValid(j))
{
vMarkColors[j]=mcolor;
Refresh();
}
}
Color Scatter::GetMarkColor(const int& j) const
{
if(IsValid(j))
{
return vMarkColors[j];
}
throw (Exc(t_("Invalid series index!")));
}
void Scatter::SetShowMark(const int& j, const bool& show)
{
if(IsValid(j))
{
vShowMark[j]=show;
Refresh();
}
}
bool Scatter::IsMarkShow(const int& j) const throw (Exc)
{
if(!IsValid(j))
throw (Exc(t_("Invalid series index!")));
return vShowMark[j];
}
void Scatter::SetJoin(const int& j, const bool& join)
{
if(IsValid(j))
{
vJoin[j]=join;
Refresh();
}
}
bool Scatter::IsJoined(const int& j) const throw (Exc)
{
if(!IsValid(j))
throw (Exc(t_("Invalid series index!")));
return vJoin[j];
}
void Scatter::SetSmooth(const int& j, const bool& smooth)
{
if(IsValid(j))
{
vSmooth[j]=smooth;
Refresh();
}
}
bool Scatter::IsSmooth(const int& j) const throw (Exc)
{
if(!IsValid(j))
throw (Exc(t_("Invalid series index!")));
return vSmooth[j];
}
void Scatter::SetDataPrimaryY(const int& j, const bool& primary)
{
if(IsValid(j))
{
vPPrimaryY[j]=primary;
Refresh();
}
}
Scatter &Scatter::SetDataPrimaryY(const bool& primary)
{
SetDataPrimaryY(vPPrimaryY.GetCount()-1, primary);
return *this;
}
bool Scatter::IsDataPrimaryY(const int& j) const throw (Exc)
{
if(!IsValid(j))
throw (Exc(t_("Invalid series index!")));
return vPPrimaryY[j];
}
void Scatter::SetSequentialX(const int& j, const bool& sequential)
{
if(IsValid(j))
{
vPSequential[j]=sequential;
Refresh();
}
}
Scatter &Scatter::SetSequentialX(const bool& sequential)
{
SetSequentialX(vPSequential.GetCount()-1, sequential);
return *this;
}
Scatter &Scatter::SetSequentialXAll(const bool& sequential)
{
for (int i = 0; i < vPSequential.GetCount(); ++i)
SetSequentialX(i, sequential);
sequentialXAll = sequential;
return *this;
}
void Scatter::SetFunctPrimaryY(const int& j, const bool& primary)
{
if(IsValid(j))
{
vFPrimaryY[j]=primary;
Refresh();
}
}
Scatter &Scatter::SetFunctPrimaryY(const bool& primary)
{
SetFunctPrimaryY(vFPrimaryY.GetCount()-1, primary);
return *this;
}
bool Scatter::IsFunctPrimaryY(const int& j) const throw (Exc)
{
if(!IsValid(j))
throw (Exc(t_("Invalid series index!")));
return vFPrimaryY[j];
}
void Scatter::RemoveSeries(const int& j)
{
if (IsValid(j))
{
vPointsData.Remove(j);
vJoin.Remove(j);
vSmooth.Remove(j);
vPColors.Remove(j);
vPWidth.Remove(j);
vPThickness.Remove(j);
vLegend.Remove(j);
vMarkColors.Remove(j);
vShowMark.Remove(j);
vMarkStyles.Remove(j);
vPPrimaryY.Remove(j);
vPPattern.Remove(j);
vPSequential.Remove(j);
Refresh();
}
}
void Scatter::RemoveAllSeries()
{
vPointsData.Clear();
vJoin.Clear();
vSmooth.Clear();
vPColors.Clear();
vPWidth.Clear();
vPThickness.Clear();
vLegend.Clear();
vMarkColors.Clear();
vShowMark.Clear();
vMarkStyles.Clear();
vPPrimaryY.Clear();
vPSequential.Clear();
vPPattern.Clear();
Refresh();
}
void Scatter::PlotFunction(PlotFunc f, const String& legend, const class::Color& fcolor, const int& weight)
{
Vector<XY> series;
vFunctionData.AddPick(series);
vAdress.Add(f);
vFColors.Add(fcolor);
vFThickness.Add(weight);
vFLegend.Add(legend);
vFPrimaryY.Add(true);
vFPattern.Add(LINE_SOLID);
Refresh();
}
void Scatter::PlotParaFunction(PlotParamFunc f, const String& legend, const class::Color& fcolor, const int& weight,const int& Np)
{
double t;//t must be choosed between [0,1]
Vector<XY> series;
for (int i=0; i<=Np;i++)
{
t=(double)i/Np;
//series<<pf(t);
f(series.Add(), t);
}
vFColors.Add(fcolor);
vFThickness.Add(weight);
vFunctionData.AddPick(series);
vFPrimaryY.Add(true);
vFPattern.Add(LINE_SOLID);
vFLegend.Add(legend);
Refresh();
}
Scatter &Scatter::SetFunctPattern(const String pattern)
{
vFPattern[vFPattern.GetCount()-1] = pattern;
return *this;
}
void Scatter::RemoveFSeries(const int& j)
{
if (!vFunctionData.IsEmpty() && vFunctionData.GetCount()>j)
{
if(vFunctionData[j].IsEmpty())
{
int nf=-1;
for(int i=0; i<=j; i++)
{
if (vFunctionData[i].IsEmpty())
nf++;
}
vAdress.Remove(nf);
}
vFunctionData.Remove(j);
vFColors.Remove(j);
vFThickness.Remove(j);
vFLegend.Remove(j);
vFPrimaryY.Remove(j);
vFPattern.Remove(j);
Refresh();
}
}
void Scatter::RemoveAllFSeries()
{
vAdress.Clear();
vFunctionData.Clear();
vFColors.Clear();
vFThickness.Clear();
vFLegend.Clear();
vFPrimaryY.Clear();
vFPattern.Clear();
Refresh();
}
Drawing Scatter::GetDrawing() const
{
DrawingDraw ddw(6*GetSize());
SetDrawing (ddw, 6);
return ddw;
}
Image Scatter::GetImage(const int& scale)
{
bool aa=antialiasing;
antialiasing=false;
ImageDraw idraw(scale*GetSize());
SetDrawing (idraw,scale);
antialiasing=aa;
return idraw;
}
#ifdef PLATFORM_WIN32
void Scatter::SaveAsMetafile(const char* file) const
{
GuiLock __;
WinMetaFileDraw wmfd;
wmfd.Create(6*GetSize().cx,6*GetSize().cy,"Scatter","chart",file);
SetDrawing (wmfd, 6);
wmfd.Close();
}
void Scatter::SaveToClipboard(bool saveAsMetafile)
{
GuiLock __;
if (saveAsMetafile) {
WinMetaFileDraw wmfd;
wmfd.Create(6*GetSize().cx,6*GetSize().cy,"Scatter","chart");
SetDrawing (wmfd, 6);
WinMetaFile wmf = wmfd.Close();
wmf.WriteClipboard();
} else {
Image img = GetImage(3);
WriteClipboardImage(img);
}
}
#else
void Scatter::SaveToClipboard(bool)
{
GuiLock __;
Image img = GetImage(3);
WriteClipboardImage(img);
}
#endif
double Scatter::GetXByPoint(const int x)
{
return (x-GetH_Border())*GetXRange()/(GetSize().cx-2*GetH_Border()-1)+GetXMin();
}
double Scatter::GetYByPoint(const int y)
{
return (GetSize().cy-GetV_Border()-y-1)*GetYRange()/(GetSize().cy-2*GetV_Border()-GetTitleFont().GetHeight()-1)+GetYMin();
}
double Scatter::GetY2ByPoint(const int y)
{
return (GetSize().cy-GetV_Border()-y-1)*GetY2Range()/(GetSize().cy-2*GetV_Border()-GetTitleFont().GetHeight()-1)+GetYMin2();
}
double Scatter::GetXPointByValue(const double x)
{
return (x-GetXMin())/GetXRange()*(GetSize().cx-2*GetH_Border()-1)+GetH_Border();
}
double Scatter::GetYPointByValue(const double y)
{
return (GetSize().cy-GetV_Border()-1)-(y-GetYMin())/GetYRange()*(GetSize().cy-2*GetV_Border()-GetTitleFont().GetHeight()-1);
}
void Scatter::Paint(Draw& w)
{
SetDrawing(w,1);
}
Scatter &Scatter::ShowInfo(bool show)
{
paintInfo=show;
return *this;
}
void Scatter::ProcessPopUp(const Point & pt)
{
double _x= (popLT.x-px)*xRange/(GetSize().cx-2*px-1)+xMin;
double _y= (GetSize().cy-py-popLT.y-1)*yRange/(GetSize().cy-2*py-titleFont.GetHeight()-1)+yMin;
double _y2=(GetSize().cy-py-popLT.y-1)*yRange2/(GetSize().cy-2*py-titleFont.GetHeight()-1)+yMin2;
double x= (pt.x-px)*xRange/(GetSize().cx-2*px-1)+xMin;
double y= (GetSize().cy-py-pt.y-1)*yRange/(GetSize().cy-2*py-titleFont.GetHeight()-1)+yMin;
double y2=(GetSize().cy-py-pt.y-1)*yRange2/(GetSize().cy-2*py-titleFont.GetHeight()-1)+yMin2;
double dx = x - _x;
double dy = y - _y;
double dy2 = y2 - _y2;
if(logX) {
x = pow(10.0, x);
_x = pow(10.0, _x);
dx = pow(10.0, dx);
}
if(logY) {
y = pow(10.0, y);
_y = pow(10.0, _y);
dy = pow(10.0, dy);
}
if(logY2) {
y2 = pow(10.0, y2);
_y2 = pow(10.0, _y2);
dy2 = pow(10.0, dy2);
}
String strx, _strx, dstrx, stry, _stry, dstry;
if (cbModifFormatX) {
cbModifFormatX(strx, 0, x); strx.Replace("\n", " ");
cbModifFormatX(_strx, 0, _x); _strx.Replace("\n", " ");
} else {
strx = VariableFormatX(x);
_strx = VariableFormatX(_x);
}
if (cbModifFormatDeltaX) {
cbModifFormatDeltaX(dstrx, 0, dx); dstrx.Replace("\n", " ");
} else {
dstrx = VariableFormatX(dx);
}
if (cbModifFormatY) {
cbModifFormatY(stry, 0, y); stry.Replace("\n", " ");
cbModifFormatY(_stry, 0, _y); _stry.Replace("\n", " ");
} else {
stry = VariableFormatY(y);
_stry = VariableFormatY(_y);
}
if (cbModifFormatDeltaY) {
cbModifFormatDeltaY(dstry, 0, dy); dstry.Replace("\n", " ");
} else {
dstry = VariableFormatY(dy);
}
String str= popTextX + ": " + _strx;
if (strx != _strx)
str << "; " + popTextX + "': " + strx + "; Δ" + popTextX + ": " + dstrx;
str << "\n" + popTextY + ": " + _stry;
if (stry != _stry)
str << "; " + popTextY + "': " + stry + "; Δ" + popTextY + ": " + dstry;
if (drawY2Reticle) {
String stry2, _stry2, dstry2;
if (cbModifFormatY2) {
cbModifFormatY2(stry2, 0, y2); stry2.Replace("\n", " ");
cbModifFormatY2(_stry2, 0, _y2); _stry2.Replace("\n", " ");
} else {
stry2 = VariableFormatY2(y2);
_stry2 = VariableFormatY2(_y2);
}
if (cbModifFormatDeltaY2) {
cbModifFormatDeltaY2(dstry2, 0, dy2); dstry2.Replace("\n", " ");
} else {
dstry2 = VariableFormatY(dy2);
}
str << "\n" + popTextY2 + ": " + _stry2;
if (stry2 != _stry2)
str << "; " + popTextY2 + ": " + stry2 + "; Δ" + popTextY2 + ": " + dstry2;
}
const Point p2 = pt+offset;
popText.SetText(str).Move(this,p2.x,p2.y);
}
void Scatter::DoMouseAction(bool down, Point pt, MouseAction action, int value)
{
switch (action) {
case SCROLL: Scrolling(down, pt);
break;
case ZOOM_H_ENL:
case ZOOM_H_RED: MouseZoom(value, true, false);
break;
case ZOOM_V_ENL:
case ZOOM_V_RED: MouseZoom(value, false, true);
break;
case SHOW_INFO: LabelPopUp(down, pt);
break;
case CONTEXT_MENU: if(showContextMenu)
MenuBar::Execute(THISBACK(ContextMenu));
break;
}
}
bool Scatter::SetMouseBehavior(MouseBehaviour *_mouseBehavior)
{
if (!_mouseBehavior)
return false;
int i;
for (i = 0; _mouseBehavior[i].action != NO_ACTION && i < MAX_MOUSEBEHAVIOR; ++i) ;
if (i == MAX_MOUSEBEHAVIOR)
return false;
mouseBehavior = _mouseBehavior;
return true;
}
void Scatter::ProcessMouse(bool down, Point &pt, bool ctrl, bool alt, bool shift, bool left, bool middle, int middleWheel, bool right)
{
for (int i = 0; mouseBehavior[i].action != NO_ACTION && i < MAX_MOUSEBEHAVIOR; ++i) {
if (mouseBehavior[i].ctrl == ctrl && mouseBehavior[i].alt == alt &&
mouseBehavior[i].shift == shift && mouseBehavior[i].left == left &&
mouseBehavior[i].middle == middle && mouseBehavior[i].right == right &&
((mouseBehavior[i].middleWheel == 0) || mouseBehavior[i].middleWheel == ((middleWheel > 0) - (middleWheel < 0))))
DoMouseAction(down, pt, mouseBehavior[i].action, middleWheel);
}
}
void Scatter::LabelPopUp(bool down, Point &pt)
{
if (down) {
if(paintInfo && px <=pt.x && pt.x<= GetSize().cx-px && (py + titleFont.GetHeight())<=pt.y && pt.y<= GetSize().cy-py)
{
popText.AppearOnly(this);
isLabelPopUp = true;
if (IsNull(popLT))
popLT = pt;
popRB = pt;
ProcessPopUp(pt);
}
} else {
if(paintInfo && isLabelPopUp)
{
popText.Close();
isLabelPopUp = false;
popLT = popRB = Null;
Refresh();
}
}
}
#ifdef PLATFORM_LINUX
#include <X11/cursorfont.h>
#endif
void Scatter::Scrolling(bool down, Point &pt, bool isOut)
{
static Image mouseImg;
if (down) {
if((mouseHandlingX || mouseHandlingY) && px <=pt.x && pt.x<= GetSize().cx-px && (py + titleFont.GetHeight())<=pt.y && pt.y<= GetSize().cy-py)
{
butDownX = pt.x;
butDownY = pt.y;
isScrolling = true;
INTERLOCKED {
#ifdef PLATFORM_WIN32
static Image img = Win32Cursor(IDC_SIZEALL);
#else
static Image img = X11Cursor(XC_fleur);
#endif
mouseImg = Ctrl::OverrideCursor(img);
}
}
} else {
if (isScrolling) {
if (!isOut)
MouseMove(pt, 0);
isScrolling = false;
Ctrl::OverrideCursor(mouseImg);
}
}
}
void Scatter::LeftDown(Point pt, dword keyFlags)
{
ProcessMouse(true, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, true, false, 0, false);
}
void Scatter::LeftUp(Point pt, dword keyFlags)
{
ProcessMouse(false, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, true, false, 0, false);
}
void Scatter::MiddleDown(Point pt, dword keyFlags)
{
ProcessMouse(true, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, false, true, 0, false);
}
void Scatter::MiddleUp(Point pt, dword keyFlags)
{
ProcessMouse(false, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, false, true, 0, false);
}
void Scatter::RightDown(Point pt, dword keyFlags)
{
ProcessMouse(true, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, false, false, 0, true);
}
void Scatter::RightUp(Point pt, dword keyFlags)
{
ProcessMouse(false, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, false, false, 0, true);
}
void Scatter::MouseWheel(Point pt, int zdelta, dword keyFlags)
{
if (zdelta == 0)
return;
ProcessMouse(true, pt, keyFlags & K_CTRL, keyFlags & K_ALT, keyFlags & K_SHIFT, false, false, zdelta, false);
}
void Scatter::MouseMove(Point pt, dword)
{
if (isScrolling)
{
int shiftX = pt.x - butDownX;
if (mouseHandlingX && shiftX != 0) {
double deltaX = shiftX*xRange/(GetSize().cx - 2*px - 1);
xMin -= deltaX;
xMinUnit += deltaX;
AdjustMinUnitX();
butDownX = pt.x;
}
int shiftY = pt.y - butDownY;
if (mouseHandlingY && shiftY != 0) {
double deltaY = -shiftY*yRange/(GetSize().cy - 2*py - 1);
yMin -= deltaY;
yMinUnit += deltaY;
AdjustMinUnitY();
if (drawY2Reticle) {
double deltaY2 = -shiftY*yRange2/(GetSize().cy - 2*py - 1);
yMin2 -= deltaY2;
yMinUnit2 += deltaY2;
AdjustMinUnitY2();
}
butDownY = pt.y;
}
if ((mouseHandlingX && shiftX != 0) || (mouseHandlingY && shiftY != 0)) {
WhenSetXYMin();
Refresh();
WhenZoomScroll();
}
}
if(isLabelPopUp) {
if (paintInfo && px <=pt.x && pt.x<= GetSize().cx-px && (py + titleFont.GetHeight())<=pt.y && pt.y<= GetSize().cy-py)
{
if (IsNull(popLT))
popLT = pt;
popRB = pt;
popText.AppearOnlyOpen(this);
ProcessPopUp(pt);
Refresh();
}
}
}
void Scatter::MouseLeave()
{
Point p = Null;
Scrolling(false, p, true);
}
void Scatter::MouseZoom(int zdelta, bool hor, bool ver)
{
double scale = zdelta > 0 ? zdelta/100. : -100./zdelta;
Zoom(scale, mouseHandlingX, mouseHandlingY);
}
void Scatter::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));
//mouseX = mouseX && (!mouseHandlingY || mouseY);
//mouseY = mouseY && (!mouseHandlingX || mouseX);
if (mouseX)
{
double oldXMin = xMin;
xMin += xRange*(1-scale)/2.;
xMinUnit = oldXMin + xMinUnit - xMin;
AdjustMinUnitX();
xRange *= scale;
}
if (mouseY)
{
yRange *= scale;
yRange2 *= scale;
}
if (mouseX || mouseY) {
WhenSetRange();
Refresh();
WhenZoomScroll();
}
}
void Scatter::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();
}
}
Image Scatter::CursorImage(Point p, dword keyflags)
{
return ChartImg::cursor1();
}
void Scatter::Circle(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
int radius = fround(scale*size/6); int radius2 = radius/2;
w.DrawEllipse(cp.x-radius2, cp.y-radius2, radius, radius, markColor, 1, markColor);
}
void Scatter::Square(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
Vector <Point> p;
p<<Point(cp.x-int(size*scale/12+0.5),cp.y-int(size*scale/12+0.5))<<Point(cp.x+int(size*scale/12+0.5),cp.y-int(size*scale/12+0.5))
<<Point(cp.x+int(size*scale/12+0.5),cp.y+int(size*scale/12+0.5))<<Point(cp.x-int(size*scale/12+0.5),cp.y+int(size*scale/12+0.5))
<<Point(cp.x-int(size*scale/12+0.5),cp.y-int(size*scale/12+0.5));
w.DrawPolyline(p,scale/2,markColor);
}
void Scatter::Rectangle(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
w.DrawRect(cp.x-int(size*scale/12+0.5),cp.y-int(size*scale/12+0.5),int(size*scale/6+1.5),int(size*scale/6+1.5),markColor);
}
void Scatter::Triangle(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
Vector <Point> p;
p<<Point(cp.x,cp.y-int(size*scale/12+0.5))<<Point(cp.x+int(size*scale/12+0.5),cp.y+int(0.6*size*scale/12+0.5))
<<Point(cp.x-int(size*scale/12+0.5),cp.y+int(0.6*size*scale/12+0.5))
<<Point(cp.x,cp.y-int(size*scale/12+0.5));
w.DrawPolyline(p,scale/2,markColor);
}
void Scatter::Cross(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
w.DrawLine(cp.x-int(size*scale/12+0.5),cp.y,cp.x+int(size*scale/12+1.5),cp.y,scale,markColor);
w.DrawLine(cp.x,cp.y-int(size*scale/12+0.5),cp.x,cp.y+int(size*scale/12+1.5),scale,markColor);
}
void Scatter::XFunct(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
w.DrawLine(cp.x-int(size*scale/12+0.5),cp.y-int(size*scale/12+0.5),cp.x+int(size*scale/12+1.5),cp.y+int(size*scale/12+1.5),scale,markColor);
w.DrawLine(cp.x+int(size*scale/12+0.5),cp.y-int(size*scale/12+0.5),cp.x-int(size*scale/12+1.5),cp.y+int(size*scale/12+1.5),scale,markColor);
}
void Scatter::Rhomb(Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
Vector <Point> p;
p<<Point(cp.x,cp.y-int(size*scale/12+0.5))<<Point(cp.x+int(size*scale/12+0.5),cp.y)
<<Point(cp.x,cp.y+int(size*scale/12+0.5))<<Point(cp.x-int(size*scale/12+0.5),cp.y)
<<Point(cp.x,cp.y-int(size*scale/12+0.5));
w.DrawPolyline(p,scale,markColor);
}
inline void Scatter::DrawMark(const int& style, Draw& w, const int& scale, const Point& cp, const int& size, const class::Color& markColor)const
{
void (Scatter::*ptMark) (Draw&, const int&, const Point&, const int&, const class::Color&)const=NULL;
switch (style)
{
case CIRCLE :ptMark=&Scatter::Circle; break;
case RECTANGLE :ptMark=&Scatter::Rectangle; break;
case SQUARE : ptMark=&Scatter::Square; break;
case TRIANGLE : ptMark=&Scatter::Triangle; break;
case CROSS : ptMark=&Scatter::Cross; break;
case X : ptMark=&Scatter::XFunct; break;
case RHOMB:ptMark=&Scatter::Rhomb; break;
default : ptMark=&Scatter::Circle; break;
}
(*this.*ptMark)(w,scale,cp,size,markColor);
}
Scatter &Scatter::SetMouseHandling(bool valx, bool valy)
{
mouseHandlingX = valx;
mouseHandlingY = valy;
return *this;
}
void Scatter::ContextMenu(Bar& bar)
{
if (mouseHandlingX || mouseHandlingY) {
bar.Add(t_("Fit to data"), ChartImg::ShapeHandles(), THISBACK1(FitToData, mouseHandlingY));
bar.Add(t_("Zoom +"), ChartImg::ZoomPlus(), THISBACK3(Zoom, 1/1.2, true, mouseHandlingY));
bar.Add(t_("Zoom -"), ChartImg::ZoomMinus(), THISBACK3(Zoom, 1.2, true, mouseHandlingY));
bar.Add(t_("Scroll Left"), ChartImg::LeftArrow(), THISBACK2(Scroll, -0.2, 0));
bar.Add(t_("Scroll Right"), ChartImg::RightArrow(), THISBACK2(Scroll, 0.2, 0));
if (mouseHandlingY) {
bar.Add(t_("Scroll Up"), ChartImg::UpArrow(), THISBACK2(Scroll, 0, -0.2));
bar.Add(t_("Scroll Down"), ChartImg::DownArrow(), THISBACK2(Scroll, 0, 0.2));
}
bar.Separator();
}
bar.Add(t_("Copy"), ChartImg::Copy(), THISBACK1(SaveToClipboard, false)).Key(K_CTRL_C);
bar.Add(t_("Save to file"), ChartImg::Save(), THISBACK1(SaveToImage, Null));
}
void Scatter::SaveToImage(String fileName)
{
GuiLock __;
if (IsNull(fileName)) {
FileSel fs;
fs.Type("PNG file", "*.png");
fs.Type("JPEG file", "*.jpg");
if(!fs.ExecuteSaveAs(t_("Saving plot to PNG or JPEG file"))) {
Exclamation(t_("Plot has not been saved"));
return;
}
fileName = fs;
}
if (GetFileExt(fileName) == ".png") {
PNGEncoder encoder;
encoder.SaveFile(fileName, GetImage(3));
} else if (GetFileExt(fileName) == ".jpg") {
JPGEncoder encoder(90);
encoder.SaveFile(fileName, GetImage(3));
} else
Exclamation(Format(t_("File format %s not found"), GetFileExt(fileName)));
}
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;
}
double Dist(const Pointf &p0, const Pointf &p1)
{
return sqrt(sqr(p0.x-p1.x) + sqr(p0.y-p1.y));
}
inline bool Even(int val) {return !(val%2);}
Vector <double> GetLinePattern(String pattern)
{
Vector <double> ret;
ret.Add();
int retPos = 0;
ret[0] = 2;
for (int i = 1; i < pattern.GetCount(); ++i) {
if (pattern[i] != pattern[i-1]) {
ret.Add();
retPos++;
ret[retPos] = 0;
}
ret[retPos] += 2;
}
return ret;
}
Vector <double> &GetPatternArray(String pattern)
{
static VectorMap <String, Vector <double> > pats;
int pos = pats.Find(pattern);
if (pos < 0) {
pats.Add(pattern, GetLinePattern(pattern));
pos = pats.GetCount()-1;
}
return pats.GetValues()[pos];
}
void Scatter::DrawLineX(Draw& w, const int x0, const int y0, const int x1, const int y1, int thick, const class::Color &color, String pattern, const int &scale)
{
Vector<Point> p;
p.SetCount(2);
p[0].x = x0;
p[0].y = y0;
p[1].x = x1;
p[1].y = y1;
DrawPolylineX(w, p, thick, color, pattern, scale);
}
void Scatter::DrawPolylineX(Draw& w, const Vector<Point> &p, int thick, const class::Color &color, String pattern, const int &scale)
{
if (pattern == LINE_SOLID)
w.DrawPolyline(p, thick, color);
else {
Vector <double> &pat = GetPatternArray(pattern);
int iPat = 0;
double len = pat[0]*scale; // Pixels por barra
Pointf begin, end;
begin = p[0];
for (int i = 1; i < p.GetCount();) {
double d = Dist(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, thick, color);
if (d >= len) {
iPat++;
if (iPat == pat.GetCount())
iPat = 0;
len = pat[iPat]*scale;
}
begin = end;
}
}
}
void Scatter::Plot(Draw& w, const int& scale,const int& l,const int& h)const
{
double d1=xRange/xMajorUnit;
double d2=yRange/yMajorUnit;
w.DrawRect(1,1,l-2,h-1,plotAreaColor); //grosimea liniei nu este scalata
int gW=fround(gridWidth*scale/6);
if(gridWidth<0)
gW=gridWidth;
Vector<Point> p;
p.SetCount(2);
if (drawVGrid)
for(int i=0; xMinUnit+i*xMajorUnit < xRange;i++) {
p[0].x = fround(l*xMinUnit/xRange+i*l/d1); p[0].y = 0;
p[1].x = fround(l*xMinUnit/xRange+i*l/d1); p[1].y = h;
DrawPolylineX(w, p, gW, gridColor, "oo..", scale);
}
if (drawHGrid)
for(int i=0; yMinUnit+i*yMajorUnit < yRange;i++) {
p[0].x = 0; p[0].y = fround(-h*yMinUnit/yRange + h-i*h/d2);
p[1].x = l; p[1].y = fround(-h*yMinUnit/yRange + h-i*h/d2);
DrawPolylineX(w, p, gW, gridColor, "oo..", scale);
}
w.DrawLine(0, h, l, h, scale, Black);
w.DrawLine(0, 0, l, 0, scale, Black);
w.DrawLine(0, 0, 0, h, scale, Black);
w.DrawLine(l, 0, l, h+1, scale, Black);
w.Clip(Rect(0,0,l,h));
int ix;//int x points coordinates
int iy;//int y points coordinates
if (!vPointsData.IsEmpty()){
for (int j=0; j<vPointsData.GetCount(); j++){
Vector<Point> p1;
int imin, imax;
if (vPSequential[j]) {
imin = imax = Null;
for (int i = 1; i<vPointsData[j].GetCount()-1; ++i) {
if (IsNull(imin)) {
if (vPointsData[j][i].x >= xMin)
imin = i-1;
} else if (IsNull(imax)) {
if (vPointsData[j][i].x >= xMin+xRange)
imax = i+1;
}
}
if (IsNull(imin))
imin = 0;
if (IsNull(imax))
imax = vPointsData[j].GetCount();
} else {
imin = 0;
imax = vPointsData[j].GetCount();
}
int numV;
if (fastViewX)
numV = 1+(imax-imin)/l;
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 += vPointsData[j][i+ii].y;
yy /= double(ii);
xx = (vPointsData[j][i].x + vPointsData[j][i+ii-1].x)/2;
} else {
xx = vPointsData[j][i].x;
yy = vPointsData[j][i].y;
}
ix=fround(l*(xx-xMin)/xRange);
if (vPPrimaryY[j])
iy=fround(h*(yy-yMin)/yRange);
else
iy=fround(h*(yy-yMin2)/yRange2);
p1<<Point(ix,h-iy);
}
if(vJoin[j])
{
if(vSmooth[j]&&vPointsData[j].GetCount()>2)
{
Vector<Point> p2;
Vector<XY> v(Cubic(vPointsData[j]));
for (int i=0; i<v.GetCount(); i++)
{
ix=fround(l*(v[i].x-xMin)/xRange);
if (vPPrimaryY[j])
iy=fround(h*(v[i].y-yMin)/yRange);
else
iy=fround(h*(v[i].y-yMin2)/yRange2);
p2<<Point(ix,h-iy);
}
if(!p2.IsEmpty())
DrawPolylineX(w, p2,fround(scale*vPThickness[j]/6),vPColors[j],vPPattern[j], scale);
} else if (!p1.IsEmpty())
DrawPolylineX(w, p1,fround(scale*vPThickness[j]/6),vPColors[j],vPPattern[j], scale);
}
if(vShowMark[j])
if (vPWidth[j] >= 1) {
for (int i=0; i<(imax-imin)/numV; i++)
DrawMark(vMarkStyles[j],w,scale,p1[i],vPWidth[j],vMarkColors[j]);
}
}
}
int nf=0; //number of functions to draw
for (int j=0; j<vFunctionData.GetCount(); j++){
if(!vFunctionData[j].IsEmpty())
{
Vector<Point> p1;
for (int i=0; i<vFunctionData[j].GetCount(); i++){
ix=fround(l*(vFunctionData[j][i].x-xMin)/xRange);
if (vFPrimaryY[j])
iy=fround(h*(vFunctionData[j][i].y-yMin)/yRange);
else
iy=fround(h*(vFunctionData[j][i].y-yMin2)/yRange2);
p1<<Point(ix,h-iy);
}
DrawPolylineX(w, p1,fround(scale*vFThickness[j]/6),vFColors[j],vFPattern[j], scale);
}
else
{
Vector<Point> p1;
for(int i=0; i<l; i++)
{
double x=xMin+i*(xRange/l);
double y;
if (vFPrimaryY[j])
{
vAdress[nf](y, x);
iy = fround(h*(y-yMin)/yRange);
}
else
{
vAdress[nf](y, x);
iy = fround(h*(y-yMin2)/yRange2);
}
p1<<Point(i,h-iy);
}
DrawPolylineX(w, p1,fround(scale*vFThickness[j]/6),vFColors[j],vFPattern[j], scale);
nf++;
}
}
if (!IsNull(popLT) && popLT != popRB) {
int x0 = px;
int y0 = py + titleFont.GetHeight();
int rfrom = min(popLT.y-y0, popRB.y-y0);
int rto = max(popLT.y-y0, popRB.y-y0);
for (int r = rfrom+scale*5; r < rto; r += scale*5)
DrawLineX(w, popLT.x-x0, r, popRB.x-x0, r, scale, SColorHighlight, "o..", scale);
DrawLineX(w, 1, popLT.y-y0, l, popLT.y-y0, scale, SColorHighlight, "o.", scale);
DrawLineX(w, 1, popRB.y-y0, l, popRB.y-y0, scale, SColorHighlight, "o.", scale);
DrawLineX(w, popLT.x-x0, 1, popLT.x-x0, h, scale, SColorHighlight, "o.", scale);
DrawLineX(w, popRB.x-x0, 1, popRB.x-x0, h, scale, SColorHighlight, "o.", scale);
}
w.End();
}
Vector<XY> Scatter::Cubic(const Vector<XY>& DataSet, const int& fineness,double tension) const
{
Vector<XY> OutSet;
if(DataSet.GetCount()>2) {
OutSet<<DataSet[0];
double t;
double a0,a1,a2,a3;
double b0,b1,b2,b3;
a2=tension*(DataSet[1].y-DataSet[0].y);
a0=a2+tension*(DataSet[2].y-DataSet[0].y)+2*DataSet[0].y-2*DataSet[1].y;
a1=-2*a2- tension*(DataSet[2].y-DataSet[0].y)-3*DataSet[0].y+3*DataSet[1].y;
a3=DataSet[0].y;
b2=tension*(DataSet[1].x-DataSet[0].x);
b0=b2+tension*(DataSet[2].x-DataSet[0].x)+2*DataSet[0].x-2*DataSet[1].x;
b1=-2*b2- tension*(DataSet[2].x-DataSet[0].x)-3*DataSet[0].x+3*DataSet[1].x;
b3=DataSet[0].x;
for (int n=1; n<=fineness; n++)
{
t=(double)n/fineness;
OutSet<<XY(b0*t*t*t+b1*t*t+b2*t+b3, a0*t*t*t+a1*t*t+a2*t+a3);
}
for (int i=3; i<DataSet.GetCount(); i++)
{
a2=tension*(DataSet[i-1].y-DataSet[i-3].y);
a0=a2+tension*(DataSet[i].y-DataSet[i-2].y)+2*DataSet[i-2].y-2*DataSet[i-1].y;
a1=-2*a2- tension*(DataSet[i].y-DataSet[i-2].y)-3*DataSet[i-2].y+3*DataSet[i-1].y;
a3=DataSet[i-2].y;
b2=tension*(DataSet[i-1].x-DataSet[i-3].x);
b0=b2+tension*(DataSet[i].x-DataSet[i-2].x)+2*DataSet[i-2].x-2*DataSet[i-1].x;
b1=-2*b2- tension*(DataSet[i].x-DataSet[i-2].x)-3*DataSet[i-2].x+3*DataSet[i-1].x;
b3=DataSet[i-2].x;
for (int n=1; n<=fineness; n++)
{
t=(double)n/fineness;
OutSet<<XY(b0*t*t*t+b1*t*t+b2*t+b3, a0*t*t*t+a1*t*t+a2*t+a3);
}
}
int nd=DataSet.GetCount()-1;
a2=tension*(DataSet[nd].y-DataSet[nd-2].y);
a0=a2+tension*(DataSet[nd].y-DataSet[nd-1].y)+2*DataSet[nd-1].y-2*DataSet[nd].y;
a1=-2*a2- tension*(DataSet[nd].y-DataSet[nd-1].y)-3*DataSet[nd-1].y+3*DataSet[nd].y;
a3=DataSet[nd-1].y;
b2=tension*(DataSet[nd].x-DataSet[nd-2].x);
b0=b2+tension*(DataSet[nd].x-DataSet[nd-1].x)+2*DataSet[nd-1].x-2*DataSet[nd].x;
b1=-2*b2- tension*(DataSet[nd].x-DataSet[nd-1].x)-3*DataSet[nd-1].x+3*DataSet[nd].x;
b3=DataSet[nd-1].x;
for (int n=1; n<=fineness; n++)
{
t=(double)n/fineness;
OutSet<<XY(b0*t*t*t+b1*t*t+b2*t+b3, a0*t*t*t+a1*t*t+a2*t+a3);
}
}
return Vector<XY>(OutSet);
}
void ParseTextMultiline(const String &text, Font fnt, Array <String> &texts, Array <Size> &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 <Size> &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;
}
void Scatter::SetDrawing(Draw& w, const int& scale) const
{
GuiLock __;
w.DrawRect(scale*GetSize(),graphColor);
Size sz;
sz.cx=0;
sz.cy=0;
if(!title.IsEmpty())
{
Font FontTitle6;
FontTitle6=titleFont;
FontTitle6.Height(scale*titleFont.GetHeight());
FontTitle6.Width(scale*titleFont.GetWidth());
sz= GetTextSize(title, FontTitle6);
w.DrawText((scale*GetSize().cx-sz.cx)/2,scale*2,title,FontTitle6,titleColor);
}
w.Offset(Point(scale*px,scale*py+scale*titleFont.GetHeight()));
if(showLegend)
DrawLegend(w,scale);
int l=scale*GetSize().cx-2*scale*px;
int h=scale*GetSize().cy-2*scale*py-scale*titleFont.GetHeight();
Font FontLabel6;
FontLabel6=labelsFont;
FontLabel6.Height(scale*labelsFont.GetHeight());
Size lx=GetTextSize(xLabel,FontLabel6);
Size ly=GetTextSize(yLabel,FontLabel6);
Size ly2=GetTextSize(yLabel2,FontLabel6);
w.DrawText(fround((l-lx.cx)/2),h+scale*py-lx.cy-scale*2,xLabel,FontLabel6,labelsColor);
w.DrawText(scale*2-scale*px,fround((h+ly.cx)/2),900,yLabel,FontLabel6,labelsColor);
w.DrawText(fround(l+scale*px-ly2.cy-2*scale),fround((h+ly2.cx)/2),900,yLabel2,FontLabel6,labelsColor);
if (drawXReticle)
for(int i=0; xMinUnit+i*xMajorUnit<=xRange;i++){
w.DrawLine(fround(l*xMinUnit/xRange+i*l/(xRange/xMajorUnit)),
h,
fround(l*xMinUnit/xRange+i*l/(xRange/xMajorUnit)),
h+scale*4,
fround(scale/2),
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 <String> texts;
Array <Size> sizes;
ParseTextMultiline(gridLabelX, StdFont(), texts, sizes);
for (int ii = 0; ii < texts.GetCount(); ++ii) {
int cy = ii == 0 ? 0 : sizes[ii-1].cy;
w.DrawText(fround(l*xMinUnit/xRange+i*l/(xRange/xMajorUnit) - scale*sizes[ii].cx/2), h+scale*(4+ii*cy), texts[ii], Standard6, axisColor);
}
}
if (drawYReticle)
for(int i=0; yMinUnit+i*yMajorUnit<=yRange;i++){
w.DrawLine(-(scale*4),
fround(-h*yMinUnit/yRange+h-i*h/(yRange/yMajorUnit)),
0,
fround(-h*yMinUnit/yRange+h-i*h/(yRange/yMajorUnit)),
fround(scale/2),
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());
w.DrawText(-dx-scale*6, fround(-h*yMinUnit/yRange+h-i*h/(yRange/yMajorUnit))-scale*8, gridLabelY, Standard6, axisColor);
}
if (drawY2Reticle)
for(int i=0; yMinUnit+i*yMajorUnit<=yRange;i++){
w.DrawLine(l+(scale*4),
fround(-h*yMinUnit2/yRange2+h-i*h/(yRange/yMajorUnit)),
l,
fround(-h*yMinUnit2/yRange2+h-i*h/(yRange/yMajorUnit)),
fround(scale/2),
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());
w.DrawText(l+scale*10,fround(-h*yMinUnit2/yRange2+h-i*h/(yRange/yMajorUnit))-scale*8,gridLabelY2,Standard6,axisColor);
}
if(antialiasing && w.IsGui()) // IsGui() is always false in Linux
{
ImageDraw imdraw(3*l,3*h);
Plot (imdraw,3,3*l,3*h);
w.DrawImage(0,0,l,h,imdraw);
} else
Plot(w,scale,l,h);
w.End();
}
Scatter::Scatter():
title (""),
titleColor(SColorText),
graphColor(White),
titleFont(Roman(20)),
xLabel(""),yLabel(""),
labelsFont(StdFont()),
labelsColor(SColorText),
plotAreaColor(SColorLtFace),
axisColor(SColorText),
axisWidth(6),
px(30),
py(30),
xRange(100.0),
yRange(100.0),
yRange2(100.0),
xMajorUnit(xRange/10),
yMajorUnit(yRange/5),
xMin(0.0), yMin(0.0), yMin2(0.0),
xMinUnit(0.0), yMinUnit(0.0), yMinUnit2(0.0),
logX(false), logY(false), logY2(false),
cbModifFormatX(NULL),cbModifFormatY(NULL),cbModifFormatY2(NULL),
cbModifFormatDeltaX(NULL),cbModifFormatDeltaY(NULL),cbModifFormatDeltaY2(NULL),
gridColor(SColorDkShadow),
gridWidth(4),
paintInfo(false),
mouseHandlingX(false), mouseHandlingY(false), isScrolling(false), isLabelPopUp(false),
drawXReticle(true), drawYReticle(true), drawY2Reticle(false),
drawVGrid(true), drawHGrid(true),
showLegend(true),legendWeight(80),
antialiasing(false),
offset(10,12),
minXZoom(-1), maxXZoom(-1), minYZoom(-1), maxYZoom(-1), fastViewX(false),
sequentialXAll(false), popTextX("x"), popTextY("y1"), popTextY2("y2"),
popLT(Null), popRB(Null), showContextMenu(false)
{
Color(graphColor);
BackPaint();
popText.SetColor(SColorFace);
SetMouseBehavior(defaultMouse);
}