14 #define minDataRange 0.000001
41 : myCurrentID(-1), myLastValid(-1), mySize(-1),
42 myfWrapped(false), myfCurrentLast(false), myfIterating(false)
55 bool isValidData()
const
57 return myLastValid >= 0;
72 throw std::logic_error(
73 "cCircularBuffer::add() called without size buffer");
76 throw std::logic_error(
77 "cCircularBuffer::add() called during iteration");
80 if (myLastValid > mySize)
99 myfCurrentLast =
false;
100 if (myLastValid == 0)
101 myfCurrentLast =
true;
105 myCurrentID = myLastValid + 1;
106 if (myCurrentID > mySize)
108 myfCurrentLast =
false;
124 myfIterating =
false;
130 if (myCurrentID > mySize)
133 if (myCurrentID == myLastValid)
134 myfCurrentLast =
true;
145 class scaleStateMachine
168 : myState(eState::fit)
175 eState event(eEvent event)
188 myState = eState::fitzoom;
191 myState = eState::fixzoom;
201 case eState::fitzoom:
202 myState = eState::fit;
204 case eState::fixzoom:
205 myState = eState::fix;
216 myState = eState::fix;
229 myState = eState::fit;
237 throw std::runtime_error(
238 "plot scaleStateMachine unrecognized event");
259 scaleStateMachine::eState &theState;
282 XScale(scaleStateMachine &machine)
283 : theState(machine.myState)
286 void xiSet(
int min,
int max)
291 void xpSet(
int min,
int max)
301 void xi2xuSet(
double u0,
double sc)
306 void fixSet(
double min,
double max)
316 void zoom(
double umin,
double umax)
329 case scaleStateMachine::eState::fit:
331 xumax = xumin + sxi2xu * ximax;
333 sxi2xp = (double)(xpmax - xpmin) / (ximax - ximin);
334 sxu2xp = (xpmax - xpmin) / (xumax - xumin);
337 case scaleStateMachine::eState::fix:
341 xixumin = (xumin - xuximin) / sxi2xu;
342 double xixumax = (xumax - xuximin) / sxi2xu;
343 sxi2xp = (xpmax - xpmin) / (xixumax - xixumin);
344 sxu2xp = (xpmax - xpmin) / (xumax - xumin);
348 case scaleStateMachine::eState::fitzoom:
349 case scaleStateMachine::eState::fixzoom:
353 xixumin = (xumin - xuximin) / sxi2xu;
354 double xixumax = (xumax - xuximin) / sxi2xu;
355 sxi2xp = (xpmax - xpmin) / (xixumax - xixumin);
356 sxu2xp = (xpmax - xpmin) / (xumax - xumin);
362 int XI2XP(
double xi)
const
370 return round(xpmin + sxi2xp * (xi - xixumin));
372 double XP2XU(
int pixel)
const
374 return xumin + (pixel - xpmin) / sxu2xp;
376 int XU2XP(
double xu)
const
378 return round(xpmin + sxu2xp * (xu - xumin));
401 <<
"state " << (int)theState
402 <<
" xpstart " << xpmin <<
" xpmax " << xpmax
403 <<
" xistart " << ximin <<
" ximax " << ximax
404 <<
" xustart " << xumin <<
" xumax " << xumax
405 <<
" sxi2xp " << sxi2xp
406 <<
" sxi2xu " << sxi2xu
416 scaleStateMachine::eState &theState;
430 YScale(scaleStateMachine &scaleMachine)
431 : theState(scaleMachine.myState)
435 void YVrange(
double min,
double max)
439 case scaleStateMachine::eState::fit:
443 case scaleStateMachine::eState::fix:
450 double YVrange()
const
452 return yvmax - yvmin;
460 void YPrange(
int min,
int max)
467 void zoom(
double min,
double max)
473 void fixSet(
double min,
double max)
479 double YP2YV(
int pixel)
const
481 return yvmin - (ypmin - pixel) / syv2yp;
484 int YV2YP(
double v)
const
486 return ypmin + syv2yp * (v - yvmin);
506 std::cout <<
"yv " << yvmin <<
" " << yvmax
507 <<
" xp " << ypmin <<
" " << ypmax
516 case scaleStateMachine::eState::fit:
521 case scaleStateMachine::eState::fix:
526 case scaleStateMachine::eState::fitzoom:
527 case scaleStateMachine::eState::fixzoom:
532 double yvrange = yvmax - yvmin;
533 if (fabs(yvrange) < 0.00001)
540 syv2yp = -(ypmin - ypmax) / yvrange;
546 std::vector<double> tickValues()
const
548 std::vector<double> vl;
549 double rangeV = fabs(yvmax - yvmin);
550 if (rangeV < minDataRange)
557 double incV = rangeV / 4;
566 double v = tickValue;
568 v = ((int)v / 100) * 100;
570 v = ((int)v / 10) * 10;
573 if (tickValue >= yvmax)
633 void set(
const std::vector<double> &y)
635 if ((myType != eType::plot) && (myType != eType::scatter))
636 throw std::runtime_error(
"plot2d error: plot data added to non plot/scatter trace");
648 void set(
double *begin,
double *end)
650 if ((myType != eType::plot) && (myType != eType::scatter))
651 throw std::runtime_error(
"plot2d error: plot data added to non plot/scatter trace");
653 myY = std::vector(begin, end);
655 void setScatterX(
const std::vector<double> &x)
657 if (myType != eType::scatter)
658 throw std::runtime_error(
"plot2d error: plot X added to non scatter trace");
662 std::vector<double> get()
const
675 if (myType != eType::realtime)
676 throw std::runtime_error(
"plot2d error: realtime data added to non realtime trace");
677 myY[myCircular.add()] = y;
688 void add(
double x,
double y)
690 if (myType != eType::scatter)
691 throw std::runtime_error(
"plot2d error: point data added to non scatter type trace");
726 return (
int)myY.size();
735 if (0 > xfraction || xfraction > 1)
737 return myY[(int)(xfraction * myY.size())];
740 const std::vector<double> &getX()
const
744 const std::vector<double> &getY()
746 if (myType != eType::realtime)
749 static std::vector<double> ret;
751 for (
int yidx = myCircular.first();
753 yidx = myCircular.next())
754 ret.push_back(myY[yidx]);
762 std::vector<double> myX;
763 std::vector<double> myY;
764 cCircularBuffer myCircular;
774 : myThick(1), myType(eType::plot)
794 myType = eType::realtime;
803 myType = eType::scatter;
810 int &txmin,
int &txmax,
811 double &tymin,
double &tymax)
819 txmax = myY.size() - 1;
838 if (myType == eType::realtime)
840 if (!myCircular.isValidData())
848 if (myCircular.isFull())
851 auto result = std::minmax_element(
854 tymin = *result.first;
855 tymax = *result.second;
862 for (
int idx = myCircular.first();
864 idx = myCircular.next())
876 auto result = std::minmax_element(
879 tymin = *result.first;
880 tymax = *result.second;
908 void enable(
bool f =
true)
921 void setGrid(
bool f =
true)
938 if (myOrient == eOrient::horz)
941 S.
line({xs.XPmin(), paxis,
943 scale = (xs.XPmax() - xs.XPmin()) / (xs.XUmax() - xs.XUmin());
951 S.
line({paxis, ys.YPmin(),
954 scale = (ys.YPmax() - ys.YPmin()) / ys.YVrange();
957 for (
double tickValue : tickValues(tickCount, myvmin, myvmax))
963 tickPixel = xs.XPmin() + scale * (tickValue - xs.XUmin());
965 {tickPixel, paxis - 5,
966 tickPixel, paxis + 5});
968 numberformat(tickValue),
969 {tickPixel, paxis + 5,
970 tickPixel + 50, paxis + 15});
977 S.
pixel(tickPixel, kp);
978 S.
pixel(tickPixel, kp + 1);
985 tickPixel = ys.YPmin() + scale * (tickValue - ys.YVmin());
986 S.
line({paxis - 5, tickPixel,
987 paxis + 5, tickPixel});
989 numberformat(tickValue),
990 {paxis - 50, tickPixel, paxis - 5, tickPixel + 15});
997 S.
pixel(kp, tickPixel);
998 S.
pixel(kp + 1, tickPixel);
1013 std::vector<double> tickValues(
1018 std::vector<double> ret;
1019 double tickinc = (max - min) / count;
1021 tickinc = floor(tickinc);
1035 std::string numberformat(
double f)
1042 int d = (int)::floor(::log10(f < 0 ? -f : f)) + 1;
1043 double order = ::pow(10., n - d);
1044 std::stringstream ss;
1045 ss << std::fixed << std::setprecision(std::max(n - d, 0)) << round(f * order) / order;
1153 :
gui(parent), myfDrag(false),
1154 myXScale(myScaleStateMachine),
1155 myYScale(myScaleStateMachine),
1156 mypBottomMarginWidth(50),
1157 mypLeftMarginWidth(70)
1160 myRightAxis.enable(
false);
1161 myRightAxis.set(axis::eOrient::vert);
1162 myLeftAxis.set(axis::eOrient::vert);
1163 myBottomAxis.set(axis::eOrient::horz);
1166 [
this](PAINTSTRUCT &ps)
1170 if (!CalcScale(ps.rcPaint.right,
1173 wex::msgbox(
"Plot has no data");
1183 for (
auto t : myTrace)
1186 drawSelectedArea(ps);
1216 myScaleStateMachine.event(scaleStateMachine::eEvent::unzoom);
1237 myTrace.push_back(t);
1254 myTrace.push_back(t);
1271 myTrace.push_back(t);
1279 myLeftAxis.setGrid(
enable);
1280 myBottomAxis.setGrid(
enable);
1290 double minX,
double maxX,
double minY,
double maxY)
1292 if (maxX <= minX || maxY <= minY)
1293 throw std::runtime_error(
1294 "plot::setFixedScale bad params");
1298 myScaleStateMachine.event(
1299 scaleStateMachine::eEvent::fix) == scaleStateMachine::eState::none)
1305 myXScale.fixSet(minX, maxX);
1306 myYScale.fixSet(minY, maxY);
1314 myScaleStateMachine.event(
1315 scaleStateMachine::eEvent::fit) == scaleStateMachine::eState::none)
1316 throw std::runtime_error(
1317 "wex plot cannot return to fit scale");
1327 mypBottomMarginWidth = pBottomMarginWidth;
1328 mypLeftMarginWidth = pLeftMarginWidth;
1331 void setXAxisLabel(
const std::string &
label)
1333 myXAxisLabel =
label;
1335 void setYAxisLabel(
const std::string &
label)
1337 myYAxisLabel =
label;
1348 myRightAxis.enable();
1349 myRightAxis.setValueRange(
1354 int traceCount()
const
1356 return (
int)myTrace.size();
1404 myXScale.xi2xuSet(start_xu, scale_xi2xu);
1418 std::vector<trace *> &traces()
1423 const YScale &yscale()
const
1431 return myXScale.XP2XU(xpixel);
1437 return myXScale.XU2XP(xu);
1443 return myYScale.YP2YV(ypixel);
1449 return myYScale.YV2YP(yu);
1459 bool CalcScale(
int w,
int h)
1468 if (!myTrace.size())
1472 myYScale.YPrange(h - mypBottomMarginWidth, 10);
1473 myXScale.xpSet(mypLeftMarginWidth, w - 50);
1477 switch (myScaleStateMachine.myState)
1479 case scaleStateMachine::eState::fit:
1481 calcDataBounds(ximin, ximax, ymin, ymax);
1482 myXScale.xiSet(ximin, ximax);
1483 myYScale.YVrange(ymin, ymax);
1484 myLeftAxis.setValueRange(ymin, ymax);
1488 case scaleStateMachine::eState::fix:
1491 case scaleStateMachine::eState::fitzoom:
1492 case scaleStateMachine::eState::fixzoom:
1499 myXScale.calculate();
1500 myYScale.calculate();
1502 myBottomAxis.setValueRange(myXScale.XUmin(), myXScale.XUmax());
1503 myLeftAxis.setValueRange(myYScale.YVmin(), myYScale.YVmax());
1512 std::vector<trace *> myTrace;
1515 scaleStateMachine myScaleStateMachine;
1523 int mypBottomMarginWidth, mypLeftMarginWidth;
1524 std::string myXAxisLabel, myYAxisLabel;
1535 void calcDataBounds(
1536 int &xmin,
int &xmax,
1537 double &ymin,
double &ymax)
1542 for (
auto &t : myTrace)
1545 double tymin, tymax;
1546 txmin = txmax = tymax = 0;
1547 tymin = std::numeric_limits<double>::max();
1548 t->bounds(txmin, txmax, tymin, tymax);
1566 if (myScaleStateMachine.event(
1567 scaleStateMachine::eEvent::zoom) ==
1568 scaleStateMachine::eState::none)
1580 myXScale.zoom(myXScale.XP2XU(myStartDragX), myXScale.XP2XU(myStopDragX));
1581 myYScale.zoom(myYScale.YP2YV(myStopDragY), myYScale.YP2YV(myStartDragY));
1585 return (myfDrag && myStopDragX > 0 && myStopDragX > myStartDragX && myStopDragY > myStartDragY);
1598 if (myYAxisLabel.length())
1601 S.
text(myYAxisLabel,
1602 {mypLeftMarginWidth - 50, myYScale.YPmax() + 30});
1605 if( myXAxisLabel.length())
1607 S.
text(myXAxisLabel,
1608 { myXScale.XPmax() -30 , myYScale.YPmin() + 30});
1624 void drawTrace(trace *t, shapes &S)
1626 S.penThick(t->thick());
1627 S.color(t->color());
1635 case trace::eType::plot:
1638 std::vector<POINT> vp;
1639 for (
auto y : t->getY())
1642 p.x = myXScale.XI2XP(xi++);
1643 p.y = myYScale.YV2YP(y);
1646 S.polyLine(vp.data(), t->size());
1650 case trace::eType::scatter:
1654 const double *px = t->getX().data();
1655 const double *py = t->getY().data();
1656 for (
int k = 0; k < t->getY().
size(); k++)
1659 int x = myXScale.XI2XP(*(px + k));
1660 int y = myYScale.YV2YP(*(py + k));
1662 {x - lnght/2, y - lnght/2,
1668 case trace::eType::realtime:
1671 for (
auto y : t->getY())
1675 double x = myXScale.XI2XP(xi++);
1676 double yp = myYScale.YV2YP(y);
1686 {(int)prevX, (
int)prev, (int)x, (
int)yp});
1696 throw std::runtime_error(
1700 void drawSelectedArea(PAINTSTRUCT &ps)
1709 S.color(0xFFFFFF ^
bgcolor());
1711 S.line({myStartDragX, myStartDragY, myStopDragX, myStartDragY});
1712 S.line({myStopDragX, myStartDragY, myStopDragX, myStopDragY});
1713 S.line({myStopDragX, myStopDragY, myStartDragX, myStopDragY});
1714 S.line({myStartDragX, myStopDragY, myStartDragX, myStartDragY});
void click(std::function< void(void)> f, bool propogate=false)
register click event handler
Definition: wex.h:280
The base class for all windex gui elements.
Definition: wex.h:900
void update()
force widget to redraw completely
Definition: wex.h:1668
std::vector< int > size()
Size of window client area.
Definition: wex.h:1719
eventhandler & events()
Get event handler.
Definition: wex.h:1742
void enable(bool f=true)
Enable/Disable, default enable.
Definition: wex.h:1039
sMouse getMouseStatus()
Get mouse status.
Definition: wex.h:1247
void bgcolor(int color)
Change background color.
Definition: wex.h:1025
A widget that displays a string.
Definition: wex.h:2676
Draw a 2D plot.
Definition: plot2d.h:1147
void setMarginWidths(int pBottomMarginWidth, int pLeftMarginWidth)
Set margin widths in pixels.
Definition: plot2d.h:1325
double pixel2Yuser(int ypixel) const
get Y user value from y pixel
Definition: plot2d.h:1441
void dragExtend(sMouse &m)
Disable auto-fit scaling and set Y minumum, maximum.
Definition: plot2d.h:1386
trace & AddScatterTrace()
Add scatter trace.
Definition: plot2d.h:1266
double pixel2Xuser(int xpixel) const
get X user value from x pixel
Definition: plot2d.h:1429
void setRightAxis(double minValue, double maxValue)
Enable drawing a right Y-axis with its own scaling.
Definition: plot2d.h:1344
void XUValues(float start_xu, float scale_xi2xu)
Set conversion from index of x value buffer to x user units.
Definition: plot2d.h:1400
void grid(bool enable)
Enable display of grid markings.
Definition: plot2d.h:1276
trace & AddRealTimeTrace(int w)
Add real time trace.
Definition: plot2d.h:1249
void clear()
Remove all traces from plot.
Definition: plot2d.h:1360
plot(gui *parent)
CTOR.
Definition: plot2d.h:1152
int yuser2pixel(double yu) const
get y pixel value from y user
Definition: plot2d.h:1447
int xuser2pixel(double xu) const
get x pixel value from y user
Definition: plot2d.h:1435
void XValues(float start_xu, float scale_xi2xu)
for backward compatability
Definition: plot2d.h:1411
void setFixedScale(double minX, double maxX, double minY, double maxY)
Set fixed scale.
Definition: plot2d.h:1289
trace & AddStaticTrace()
Add static trace.
Definition: plot2d.h:1233
Single trace to be plotted.
Definition: plot2d.h:617
void color(int clr)
set color
Definition: plot2d.h:704
double value(double xfraction)
y value at fractional position along x-axis
Definition: plot2d.h:733
void set(double *begin, double *end)
set plot data from raw buffer of doubles
Definition: plot2d.h:648
void set(const std::vector< double > &y)
set plot data
Definition: plot2d.h:633
void thick(int t)
set trace thickness in pixels
Definition: plot2d.h:714
void add(double x, double y)
add point to scatter trace
Definition: plot2d.h:688
int size() const
get number of points
Definition: plot2d.h:724
void clear()
clear data from trace
Definition: plot2d.h:697
void add(double y)
add new value to real time data
Definition: plot2d.h:673
A class that offers application code methods to draw on a window.
Definition: wex.h:529
void textHeight(int h)
Set text height.
Definition: wex.h:862
void text(const std::string &t, const std::vector< int > &v)
Draw text.
Definition: wex.h:763
void textVertical(bool f=true)
Enable / disable drawing text in vertical orientation Note: rotated text will NOT be clipped.
Definition: wex.h:847
void line(const std::vector< int > &v)
Draw line between two points.
Definition: wex.h:620
void pixel(int x, int y)
Color a pixel.
Definition: wex.h:613
void color(int r, int g, int b)
Set color for drawings.
Definition: wex.h:563
A structure containing the mouse status for event handlers.
Definition: wex.h:29