ScatterDraw: New properties

git-svn-id: svn://ultimatepp.org/upp/trunk@9665 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
koldo 2016-04-01 20:46:13 +00:00
parent 1cba02d0d0
commit 34d5d720b6
8 changed files with 155 additions and 133 deletions

View file

@ -51,6 +51,19 @@ double DataSource::Avg(Getdatafun getdata) {
return ret/count;
}
double DataSource::IsSorted(Getdatafun getdata) {
int64 num = GetCount();
if (num == 0)
return false;
if (num == 1)
return 1;
for (int i = 1; i < num; ++i) {
if (Membercall(getdata)(i) < Membercall(getdata)(i - 1))
return false;
}
return true;
}
double DataSource::Variance(Getdatafun getdata, double avg) {
if (IsNull(avg))
avg = Avg(getdata);
@ -111,17 +124,23 @@ Vector<int64> DataSource::Envelope(Getdatafun getdataY, Getdatafun getdataX, dou
}
if (IsNull(numComparisons))
continue;
if (numComparisons > 2)
ret << i;
if (numComparisons > 2) {
if (!ret.IsEmpty()) {
int64 prev_i = ret[ret.GetCount() - 1];
if (Membercall(getdataX)(prev_i) != x)
ret << i;
} else
ret << i;
}
}
return ret;
}
bool GreaterThan(double a, double b) {return a > b;}
bool LowerThan(double a, double b) {return a < b;}
bool GreaterEqualThan(double a, double b) {return a >= b;}
bool LowerEqualThan(double a, double b) {return a <= b;}
Vector<int64> DataSource::UpperEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, GreaterThan);}
Vector<int64> DataSource::LowerEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, LowerThan);}
Vector<int64> DataSource::UpperEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, GreaterEqualThan);}
Vector<int64> DataSource::LowerEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, LowerEqualThan);}
Vector<Pointf> DataSource::MovingAverage(Getdatafun getdataY, Getdatafun getdataX, double width) {
Vector<Pointf> ret;
@ -204,6 +223,8 @@ bool DataSource::SinEstim_FreqPhase(double &frequency, double &phase, double avg
lastZero = zero;
}
}
if (T == 0 || numT == 0)
return false;
T = 2*T/numT;
frequency = 2*M_PI/T;
phase = -frequency*firstZero;
@ -253,100 +274,4 @@ Vector<Pointf> DataSource::FFT(Getdatafun getdata, double tSample) {
}
bool Spline::Load(Vector<double>& xdata, Vector<double>& ydata)
{
if(!IsInputSane(xdata, ydata))
return false;
int n = xdata.GetCount() - 1;
Vector<double> h;
for(int i = 0; i < n; ++i)
h << (xdata[i+1] - xdata[i]);
Vector<double> alpha;
for(int i = 0; i < n; ++i)
alpha << (3.*(ydata[i+1] - ydata[i])/h[i] - 3*(ydata[i]-ydata[i-1])/h[i-1]);
Vector<double> l, mu, z;
l.SetCount(n+1);
mu.SetCount(n+1);
z.SetCount(n+1);
l[0] = 1;
mu[0] = 0;
z[0] = 0;
for(int i = 1; i < n; ++i) {
l[i] = 2.*(xdata[i+1] - xdata[i-1]) - h[i-1]*mu[i-1];
mu[i] = h[i]/l[i];
z[i] = (alpha[i] - h[i-1]*z[i-1])/l[i];
}
l[n] = 1.;
z[n] = 0;
Vector<double> c;
c.SetCount(n+1);
c[n] = 0;
Vector<double> b, d;
b.SetCount(n);
d.SetCount(n);
for(int j = n-1; j >= 0; --j) {
c[j] = z[j] - mu[j] * c[j+1];
b[j] = (ydata[j+1] - ydata[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3.;
d[j] = (c[j+1] - c[j])/3./h[j];
}
coeff.SetCount(n);
for(int i = 0; i < n; ++i) {
coeff[i].a = ydata[i];
coeff[i].b = b[i];
coeff[i].c = c[i];
coeff[i].d = d[i];
coeff[i].x = xdata[i];
}
return true;
}
double Spline::GetY(double x)
{
int j;
for (j = 0; j < coeff.size(); j++) {
if(coeff[j].x > x) {
if(j == 0)
j++;
break;
}
}
j--;
double dx = x - coeff[j].x;
return coeff[j].a + coeff[j].b*dx + coeff[j].c*dx*dx + coeff[j].d*dx*dx*dx;
}
bool Spline::IsInputSane(Vector<double>& xdata, Vector<double>& ydata)
{
if(xdata.IsEmpty() || ydata.IsEmpty())
return false;
if(!IsSorted(xdata))
return false;
bool first = true;
double xold;
for(int i = 0; i < xdata.GetCount(); ++i) {
double x = xdata[i];
if(first)
xold = x;
else {
first = false;
if( fabs(x - xold) < 1)
return false;
xold = x;
}
}
return true;
}
END_UPP_NAMESPACE

View file

@ -3,19 +3,6 @@
NAMESPACE_UPP
template <class T>
bool IsSorted(T& data) {
int num = data.GetCount();
if (num == 0)
return false;
if (num == 1)
return 1;
for (int i = 1; i < num; ++i) {
if (data[i] < data[i - 1])
return false;
}
return true;
}
class DataSource {
public:
@ -33,6 +20,7 @@ public:
virtual double f(double x) {NEVER(); return Null;}
virtual double f(Vector<double> zn) {NEVER(); return Null;}
virtual int64 GetCount() {NEVER(); return Null;}
bool IsEmpty() {return GetCount() == 0;}
virtual int GetznxCount(int64 id) {return 0;}
virtual int GetznyCount(int64 id) {return 0;}
virtual int GetznFixedCount() {return 0;}
@ -50,6 +38,9 @@ public:
virtual double MaxX(int64& id) {return Max(&DataSource::x, id);}
virtual double MaxX() {int64 dummy; return Max(&DataSource::x, dummy);}
virtual double IsSortedY() {return IsSorted(&DataSource::y);}
virtual double IsSortedX() {return IsSorted(&DataSource::x);}
virtual double AvgY() {return Avg(&DataSource::y);}
virtual double AvgX() {return Avg(&DataSource::x);}
virtual double StdDevY(double avg = Null) {return StdDev(&DataSource::y, avg);}
@ -63,6 +54,7 @@ public:
double Min(Getdatafun getdata, int64& id);
double Max(Getdatafun getdata, int64& id);
double Avg(Getdatafun getdata);
double IsSorted(Getdatafun getdata);
double StdDev(Getdatafun getdata, double avg = Null);
double Variance(Getdatafun getdata, double avg = Null);
Vector<int64> UpperEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width);
@ -402,18 +394,11 @@ public:
virtual inline int64 GetCount() {return numPoints;}
};
class Spline {
public:
bool Load(Vector<double>& xdata, Vector<double>& ydata);
double GetY(double x);
private:
struct Coeff{double a, b, c, d, x;};
Vector<Coeff> coeff;
bool IsInputSane(Vector<double>& xdata, Vector<double>& ydata);
struct PointfLess {
bool operator () (const Pointf& a, const Pointf& b) const { return a.x < b.x; }
};
END_UPP_NAMESPACE
#endif

View file

@ -334,6 +334,89 @@ String EvalExpr::EvalStr(String line, int numDigits) {
}
}
ExplicitEquation::FitError SplineEquation::Fit(DataSource &data, double &r2)
{
Vector<Pointf> seriesRaw;
for (int64 i = 0; i < data.GetCount(); ++i) { // Remove Nulls
if (!IsNull(data.x(i)) && !IsNull(data.y(i)))
seriesRaw << Pointf(data.x(i), data.y(i));
}
if(seriesRaw.IsEmpty())
return InadequateDataSource;
PointfLess less;
Sort(seriesRaw, less); // Sort
Vector<Pointf> series;
for (int i = 1; i < seriesRaw.GetCount(); ++i) { // Remove points with duplicate x
if (seriesRaw[i].x != seriesRaw[i - 1].x)
series << seriesRaw[i];
}
r2 = 0;
int n = int(series.GetCount()) - 1;
Buffer<double> h(n);
for(int i = 0; i < n; ++i)
h[i] = (series[i+1].x - series[i].x);
Buffer<double> alpha(n);
for(int i = 1; i < n; ++i)
alpha[i] = (3.*(series[i+1].y - series[i].y)/h[i] - 3*(series[i].y - series[i-1].y)/h[i-1]);
Buffer<double> c(n+1), l(n+1), mu(n+1), z(n+1);
l[0] = 1;
mu[0] = 0;
z[0] = 0;
for(int i = 1; i < n; ++i) {
l[i] = 2.*(series[i+1].x - series[i-1].x) - h[i-1]*mu[i-1];
mu[i] = h[i]/l[i];
z[i] = (alpha[i] - h[i-1]*z[i-1])/l[i];
}
l[n] = 1.;
z[n] = 0;
c[n] = 0;
Buffer<double> b(n), d(n);
for(int i = n-1; i >= 0; --i) {
c[i] = z[i] - mu[i] * c[i+1];
b[i] = (series[i+1].y - series[i].y)/h[i] - h[i]*(c[i+1] + 2*c[i])/3.;
d[i] = (c[i+1] - c[i])/3./h[i];
}
ncoeff = n;
coeff.Alloc(ncoeff);
for(int i = 0; i < n; ++i) {
coeff[i].x = series[i].x;
coeff[i].a = series[i].y;
coeff[i].b = b[i];
coeff[i].c = c[i];
coeff[i].d = d[i];
}
return NoError;
}
double SplineEquation::f(double x)
{
int j;
for (j = 0; j < ncoeff; j++) {
if(coeff[j].x > x) {
if(j == 0)
j++;
break;
}
}
j--;
double dx = x - coeff[j].x;
return coeff[j].a + coeff[j].b*dx + coeff[j].c*dx*dx + coeff[j].d*dx*dx*dx;
}
INITBLOCK {
ExplicitEquation::Register<LinearEquation>("LinearEquation");
ExplicitEquation::Register<PolynomialEquation2>("PolynomialEquation2");

View file

@ -18,7 +18,7 @@ public:
ImproperInputParameters = -3,
TooManyFunctionEvaluation = -4
};
FitError Fit(DataSource &series, double &r2);
virtual FitError Fit(DataSource &series, double &r2);
FitError Fit(DataSource &series) {double dummy; return Fit(series, dummy);}
virtual void GuessCoeff(DataSource &series) = 0;
@ -225,6 +225,23 @@ public:
void SetDegree(int num) {NEVER();}
};
class SplineEquation : public ExplicitEquation {
public:
SplineEquation() {}
double f(double x);
virtual String GetName() {return t_("Spline");}
void SetDegree(int num) {NEVER();}
void GuessCoeff(DataSource &series) {NEVER();}
String GetEquation(int) {return t_("Spline");}
FitError Fit(DataSource &series, double &r2);
FitError Fit(DataSource &series) {double dummy; return Fit(series, dummy);}
private:
struct Coeff{double a, b, c, d, x;};
Buffer<Coeff> coeff;
int ncoeff;
};
class EvalExpr {
public:

View file

@ -241,18 +241,19 @@ void ScatterDraw::DrawLegend(Draw& w, const Size &size, int scale) const {
}
Font italic = scaledFont;
italic.Italic();
scaledFont.Bold();
for(int row = 0, start = 0; row <= nrows; row++) {
for(int i = start; i < min(start + nlr, nlab); i++) {
int lx = rect.left + (i - start)*legendWidth + xWidth;
int ly = (rowIncSign >= 0 ? rect.top : rect.bottom) +
rowIncSign*int(rowHeight*(row + 0.6) + loclegendRowSpacing*(row + 0.5));
Vector <Point> vp;
vp << Point(lx, ly) << Point(lx + lineLen, ly);
Vector <Point> line;
line << Point(lx, ly) << Point(lx + lineLen, ly);
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(lx + scale*7, ly);
DrawPolylineOpa(w, line, scale, 1, series[i].thickness, series[i].color, series[i].dash);
Point mark_p(lx + scale*7, ly);
if (series[i].markWidth >= 1 && series[i].markPlot)
series[i].markPlot->Paint(w, scale, p, series[i].markWidth, series[i].markColor,
series[i].markPlot->Paint(w, scale, mark_p, series[i].markWidth, series[i].markColor,
series[i].markBorderWidth, series[i].markBorderColor);
Font &font = series[i].primaryY ? scaledFont : italic;
DrawText(w, lx + lineLen + xWidth, ly - scale*6, 0, legends[i], font, series[i].color);
@ -1749,7 +1750,7 @@ void DrawPolylineOpa(Draw& w, const Vector<Point> &p, int scale, double opacity,
ASSERT(!p.IsEmpty());
Color color = GetOpaqueColor(_color, background, opacity) ;
if (dash == LINE_SOLID)
w.DrawPolyline(p, fround(thick), color);
w.DrawPolyline(p, fround(thick*scale), color);
else {
Vector <double> &pat = GetDashedArray(dash);
if (pat.IsEmpty())
@ -1769,7 +1770,7 @@ void DrawPolylineOpa(Draw& w, const Vector<Point> &p, int scale, double opacity,
++i;
}
if (Even(iPat))
w.DrawLine(begin, end, fround(thick), color);
w.DrawLine(begin, end, fround(thick*scale), color);
if (d >= len) {
iPat++;
if (iPat == pat.GetCount())

View file

@ -751,6 +751,8 @@ void ScatterDraw::Plot(T& w, const Size &size, int scale)
if (drawVGrid) {
if (!isPolar) {
double x0 = plotW*xMinUnit/xRange;
if ((xRange - xMinUnit)/xMajorUnit > 20)
xMajorUnit = (xRange - xMinUnit)/20.;
for(int i = 0; xMinUnit + i*xMajorUnit < xRange; i++) {
int xg = fround(x0 + i*plotW/d1);
if (xg > 2*gridWidth && xg < plotW - 2*gridWidth)
@ -767,6 +769,8 @@ void ScatterDraw::Plot(T& w, const Size &size, int scale)
if (drawHGrid) {
if (!isPolar) {
double y0 = -plotH*yMinUnit/yRange + plotH;
if ((yRange - yMinUnit)/yMajorUnit > 20)
yMajorUnit = (yRange - yMinUnit)/20.;
for(int i = 0; yMinUnit + i*yMajorUnit < yRange; i++) {
int yg = fround(y0 - i*plotH/d2);
if (yg > 2*gridWidth && yg < plotH - 2*gridWidth)

View file

@ -68,3 +68,9 @@ caES("Racional_1")
esES("Racional_1")
euES("")
frFR("")
T_("Spline")
caES("")
esES("")
euES("")
frFR("")

View file

@ -153,4 +153,5 @@ public:
}
};
#endif