diff --git a/uppsrc/ScatterDraw/DataSource.cpp b/uppsrc/ScatterDraw/DataSource.cpp index b1983a739..1fcbef9f9 100644 --- a/uppsrc/ScatterDraw/DataSource.cpp +++ b/uppsrc/ScatterDraw/DataSource.cpp @@ -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 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 DataSource::UpperEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, GreaterThan);} -Vector DataSource::LowerEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, LowerThan);} +Vector DataSource::UpperEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, GreaterEqualThan);} +Vector DataSource::LowerEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width) {return Envelope(getdataY, getdataX, width, LowerEqualThan);} Vector DataSource::MovingAverage(Getdatafun getdataY, Getdatafun getdataX, double width) { Vector 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 DataSource::FFT(Getdatafun getdata, double tSample) { } -bool Spline::Load(Vector& xdata, Vector& ydata) -{ - if(!IsInputSane(xdata, ydata)) - return false; - - int n = xdata.GetCount() - 1; - - Vector h; - for(int i = 0; i < n; ++i) - h << (xdata[i+1] - xdata[i]); - - Vector 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 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 c; - c.SetCount(n+1); - c[n] = 0; - - Vector 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& xdata, Vector& 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 \ No newline at end of file diff --git a/uppsrc/ScatterDraw/DataSource.h b/uppsrc/ScatterDraw/DataSource.h index 038081c96..aeaf4f3a2 100644 --- a/uppsrc/ScatterDraw/DataSource.h +++ b/uppsrc/ScatterDraw/DataSource.h @@ -3,19 +3,6 @@ NAMESPACE_UPP -template -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 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 UpperEnvelope(Getdatafun getdataY, Getdatafun getdataX, double width); @@ -402,18 +394,11 @@ public: virtual inline int64 GetCount() {return numPoints;} }; -class Spline { -public: - bool Load(Vector& xdata, Vector& ydata); - double GetY(double x); - -private: - struct Coeff{double a, b, c, d, x;}; - Vector coeff; - - bool IsInputSane(Vector& xdata, Vector& ydata); +struct PointfLess { + bool operator () (const Pointf& a, const Pointf& b) const { return a.x < b.x; } }; + END_UPP_NAMESPACE #endif diff --git a/uppsrc/ScatterDraw/Equation.cpp b/uppsrc/ScatterDraw/Equation.cpp index 635e6b093..e1f253d56 100644 --- a/uppsrc/ScatterDraw/Equation.cpp +++ b/uppsrc/ScatterDraw/Equation.cpp @@ -334,6 +334,89 @@ String EvalExpr::EvalStr(String line, int numDigits) { } } + +ExplicitEquation::FitError SplineEquation::Fit(DataSource &data, double &r2) +{ + Vector 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 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 h(n); + for(int i = 0; i < n; ++i) + h[i] = (series[i+1].x - series[i].x); + + Buffer 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 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 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"); ExplicitEquation::Register("PolynomialEquation2"); diff --git a/uppsrc/ScatterDraw/Equation.h b/uppsrc/ScatterDraw/Equation.h index e46db2e09..35310cd79 100644 --- a/uppsrc/ScatterDraw/Equation.h +++ b/uppsrc/ScatterDraw/Equation.h @@ -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; + int ncoeff; +}; + class EvalExpr { public: diff --git a/uppsrc/ScatterDraw/ScatterDraw.cpp b/uppsrc/ScatterDraw/ScatterDraw.cpp index 9922ca3a6..427f49702 100644 --- a/uppsrc/ScatterDraw/ScatterDraw.cpp +++ b/uppsrc/ScatterDraw/ScatterDraw.cpp @@ -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 vp; - vp << Point(lx, ly) << Point(lx + lineLen, ly); + Vector 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 &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 &pat = GetDashedArray(dash); if (pat.IsEmpty()) @@ -1769,7 +1770,7 @@ void DrawPolylineOpa(Draw& w, const Vector &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()) diff --git a/uppsrc/ScatterDraw/ScatterDraw.h b/uppsrc/ScatterDraw/ScatterDraw.h index a1faa5af0..c3a1bbebc 100644 --- a/uppsrc/ScatterDraw/ScatterDraw.h +++ b/uppsrc/ScatterDraw/ScatterDraw.h @@ -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) diff --git a/uppsrc/ScatterDraw/ScatterDraw.t b/uppsrc/ScatterDraw/ScatterDraw.t index 902fc4a3a..fd8d3f654 100644 --- a/uppsrc/ScatterDraw/ScatterDraw.t +++ b/uppsrc/ScatterDraw/ScatterDraw.t @@ -68,3 +68,9 @@ caES("Racional_1") esES("Racional_1") euES("") frFR("") + +T_("Spline") +caES("") +esES("") +euES("") +frFR("") diff --git a/uppsrc/ScatterDraw/SeriesPlot.h b/uppsrc/ScatterDraw/SeriesPlot.h index 401e189c3..09ec3d625 100644 --- a/uppsrc/ScatterDraw/SeriesPlot.h +++ b/uppsrc/ScatterDraw/SeriesPlot.h @@ -153,4 +153,5 @@ public: } }; + #endif