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)
441 double YVrange()
const
443 return yvmax - yvmin;
451 void YPrange(
int min,
int max)
458 void zoom(
double min,
double max)
464 void fixSet(
double min,
double max)
470 double YP2YV(
int pixel)
const
472 return yvmin - (ypmin - pixel) / syv2yp;
474 int YV2YP(
double v)
const
476 return ypmin + syv2yp * (v - yvmin);
488 std::cout <<
"yv " << yvmin <<
" " << yvmax
489 <<
" xp " << ypmin <<
" " << ypmax
498 case scaleStateMachine::eState::fit:
503 case scaleStateMachine::eState::fix:
508 case scaleStateMachine::eState::fitzoom:
509 case scaleStateMachine::eState::fixzoom:
514 double yvrange = yvmax - yvmin;
515 if (fabs(yvrange) < 0.00001)
522 syv2yp = -(ypmin - ypmax) / yvrange;
578 void set(
const std::vector<double> &y)
580 if ((myType != eType::plot) && (myType != eType::scatter))
581 throw std::runtime_error(
"plot2d error: plot data added to non plot/scatter trace");
585 void setScatterX(
const std::vector<double> &x)
587 if (myType != eType::scatter)
588 throw std::runtime_error(
"plot2d error: plot X added to non scatter trace");
592 std::vector<double> get()
const
605 if (myType != eType::realtime)
606 throw std::runtime_error(
"plot2d error: realtime data added to non realtime trace");
607 myY[myCircular.add()] = y;
618 void add(
double x,
double y)
620 if (myType != eType::scatter)
621 throw std::runtime_error(
"plot2d error: point data added to non scatter type trace");
656 return (
int)myY.size();
665 if (0 > xfraction || xfraction > 1)
667 return myY[(int)(xfraction * myY.size())];
670 const std::vector<double> &getY()
672 if (myType != eType::realtime)
675 static std::vector<double> ret;
677 for (
int yidx = myCircular.first();
679 yidx = myCircular.next())
680 ret.push_back(myY[yidx]);
688 std::vector<double> myX;
689 std::vector<double> myY;
690 cCircularBuffer myCircular;
700 : myThick(1), myType(eType::plot)
720 myType = eType::realtime;
729 myType = eType::scatter;
736 int &txmin,
int &txmax,
737 double &tymin,
double &tymax)
745 txmax = myY.size() - 1;
764 if (myType == eType::realtime)
766 if (!myCircular.isValidData())
774 if (myCircular.isFull())
777 auto result = std::minmax_element(
780 tymin = *result.first;
781 tymax = *result.second;
788 for (
int idx = myCircular.first();
790 idx = myCircular.next())
802 auto result = std::minmax_element(
805 tymin = *result.first;
806 tymax = *result.second;
899 :
gui(parent), myfDrag(false),
900 myXScale(myScaleStateMachine),
901 myYScale(myScaleStateMachine)
906 [
this](PAINTSTRUCT &ps)
913 wex::msgbox(
"Plot has no data");
923 ps.rcPaint.bottom - 20);
926 for (
auto t : myTrace)
929 drawSelectedArea(ps);
955 if (myScaleStateMachine.event(scaleStateMachine::eEvent::zoom) != scaleStateMachine::eState::none)
957 double myZoomXMin = myXScale.XP2XU(myStartDragX);
958 double myZoomXMax = myXScale.XP2XU(myStopDragX);
959 double myZoomYMax = myYScale.YP2YV(myStartDragY);
960 double myZoomYMin = myYScale.YP2YV(myStopDragY);
962 myXScale.zoom(myZoomXMin, myZoomXMax);
963 myYScale.zoom(myZoomYMin, myZoomYMax);
978 myScaleStateMachine.event(scaleStateMachine::eEvent::unzoom);
999 myTrace.push_back(t);
1016 myTrace.push_back(t);
1033 myTrace.push_back(t);
1050 double minX,
double maxX,
double minY,
double maxY)
1052 if (maxX <= minX || maxY <= minY)
1053 throw std::runtime_error(
1054 "plot::setFixedScale bad params");
1058 myScaleStateMachine.event(
1059 scaleStateMachine::eEvent::fix) == scaleStateMachine::eState::none)
1065 myXScale.fixSet(minX, maxX);
1066 myYScale.fixSet(minY, maxY);
1074 myScaleStateMachine.event(
1075 scaleStateMachine::eEvent::fit) == scaleStateMachine::eState::none)
1076 throw std::runtime_error(
1077 "wex plot cannot return to fit scale");
1080 int traceCount()
const
1082 return (
int)myTrace.size();
1130 myXScale.xi2xuSet(start_xu, scale_xi2xu);
1155 if (!myTrace.size())
1159 myYScale.YPrange(h - 40, 10);
1160 myXScale.xpSet(50, w - 70);
1164 switch (myScaleStateMachine.myState)
1166 case scaleStateMachine::eState::fit:
1168 calcDataBounds(ximin, ximax, ymin, ymax);
1169 myXScale.xiSet(ximin, ximax);
1170 myYScale.YVrange(ymin, ymax);
1173 case scaleStateMachine::eState::fix:
1174 calcDataBounds(ximin, ximax, ymin, ymax);
1175 myXScale.xiSet(ximin, ximax);
1178 case scaleStateMachine::eState::fitzoom:
1179 case scaleStateMachine::eState::fixzoom:
1186 myXScale.calculate();
1187 myYScale.calculate();
1194 std::vector<trace *> &traces()
1207 return myXScale.XP2XU(xpixel);
1209 int xuser2pixel(
double xu)
const
1211 return myXScale.XU2XP(xu);
1216 return myYScale.YP2YV(ypixel);
1221 std::vector<trace *> myTrace;
1224 scaleStateMachine myScaleStateMachine;
1237 void calcDataBounds(
1238 int &xmin,
int &xmax,
1239 double &ymin,
double &ymax)
1244 for (
auto &t : myTrace)
1247 double tymin, tymax;
1248 txmin = txmax = tymax = 0;
1249 tymin = std::numeric_limits<double>::max();
1250 t->bounds(txmin, txmax, tymin, tymax);
1263 return (myfDrag && myStopDragX > 0 && myStopDragX > myStartDragX && myStopDragY > myStartDragY);
1266 std::vector<double> ytickValues()
1268 std::vector<double> vl;
1269 double mn = myYScale.YPmax();
1270 double mx = myYScale.YPmin();
1271 double range = mx - mn;
1272 if (range < minDataRange)
1279 double inc = myYScale.YVrange() / 4;
1284 tickValue = myYScale.YP2YV(myYScale.YPmin());
1295 double v = tickValue;
1297 v = ((int)v / 100) * 100;
1299 v = ((int)v / 10) * 10;
1302 if (tickValue >= mx)
1311 std::string numberformat(
double f)
1318 int d = (int)::floor(::log10(f < 0 ? -f : f)) + 1;
1319 double order = ::pow(10., n - d);
1320 std::stringstream ss;
1321 ss << std::fixed << std::setprecision(std::max(n - d, 0)) << round(f * order) / order;
1329 S.
line({50, myYScale.YPmin(),
1330 50, myYScale.YPmax()});
1332 for (
double y : ytickValues())
1334 int yp = myYScale.YV2YP(y);
1335 S.
text(numberformat(y),
1336 {0, yp - 8, 50, 15});
1341 auto kpmax = myXScale.XPmax();
1347 S.
pixel(kp + 1, yp);
1359 S.
line({myXScale.XPmin(), ypos, myXScale.XPmax(), ypos});
1364 float xmin_label_value = 0;
1365 float xmax_label_value = 100;
1368 xmin_label_value = myXScale.XUmin();
1369 xmax_label_value = myXScale.XUmax();
1371 S.
text(std::to_string((
int)xmin_label_value), {myXScale.XPmin(), ypos + 3, 50, 15});
1372 S.
text(std::to_string((
int)xmax_label_value), {myXScale.XPmax() - 25, ypos + 3, 50, 15});
1378 float xutickinc = (myXScale.XUmax() - myXScale.XUmin()) / tickCount;
1388 xutickinc = floor(xutickinc);
1390 for (
int kxtick = 0; kxtick <= tickCount; kxtick++)
1392 float tickXU = myXScale.XUmin() + kxtick * xutickinc;
1394 int xPixel = myXScale.XU2XP(tickXU);
1400 std::to_string(tickXU).substr(0, 4),
1401 {xPixel, ypos + 1, 50, 15});
1404 int k = myYScale.YPmax();
1405 k < myYScale.YPmin();
1409 S.
pixel(xPixel, k + 1);
1413 void drawTrace(trace *t, shapes &S)
1415 S.penThick(t->thick());
1416 S.color(t->color());
1424 case trace::eType::plot:
1427 std::vector<POINT> vp;
1428 for (
auto y : t->getY())
1431 p.x = myXScale.XI2XP(xi++);
1432 p.y = myYScale.YV2YP(y);
1435 S.polyLine(vp.data(), t->size());
1439 case trace::eType::scatter:
1441 for (
auto y : t->getY())
1444 {myXScale.XI2XP(xi++) - 5, myYScale.YV2YP(y) - 5,
1449 case trace::eType::realtime:
1452 for (
auto y : t->getY())
1456 double x = myXScale.XI2XP(xi++);
1457 double yp = myYScale.YV2YP(y);
1467 {(int)prevX, (
int)prev, (int)x, (
int)yp});
1477 throw std::runtime_error(
1481 void drawSelectedArea(PAINTSTRUCT &ps)
1490 S.color(0xFFFFFF ^
bgcolor());
1492 S.line({myStartDragX, myStartDragY, myStopDragX, myStartDragY});
1493 S.line({myStopDragX, myStartDragY, myStopDragX, myStopDragY});
1494 S.line({myStopDragX, myStopDragY, myStartDragX, myStopDragY});
1495 S.line({myStartDragX, myStopDragY, myStartDragX, myStartDragY});
void click(std::function< void(void)> f, bool propogate=false)
register click event handler
Definition: wex.h:276
The base class for all windex gui elements.
Definition: wex.h:824
void update()
force widget to redraw completely
Definition: wex.h:1575
eventhandler & events()
Get event handler.
Definition: wex.h:1649
void enable(bool f=true)
Enable/Disable, default enable.
Definition: wex.h:963
sMouse getMouseStatus()
Get mouse status.
Definition: wex.h:1171
void bgcolor(int color)
Change background color.
Definition: wex.h:949
Draw a 2D plot.
Definition: plot2d.h:893
double pixel2Yuser(int ypixel) const
get Y user value from y pixel
Definition: plot2d.h:1214
void dragExtend(sMouse &m)
Disable auto-fit scaling and set Y minumum, maximum.
Definition: plot2d.h:1112
trace & AddScatterTrace()
Add scatter trace.
Definition: plot2d.h:1028
bool CalcScale(int w, int h)
calculate scaling factors so plot will fit in window client area
Definition: plot2d.h:1146
double pixel2Xuser(int xpixel) const
get X user value from x pixel
Definition: plot2d.h:1205
void XUValues(float start_xu, float scale_xi2xu)
Set conversion from index of x value buffer to x user units.
Definition: plot2d.h:1126
void grid(bool enable)
Enable display of grid markings.
Definition: plot2d.h:1038
trace & AddRealTimeTrace(int w)
Add real time trace.
Definition: plot2d.h:1011
void clear()
Remove all traces from plot.
Definition: plot2d.h:1086
plot(gui *parent)
CTOR.
Definition: plot2d.h:898
void XValues(float start_xu, float scale_xi2xu)
for backward compatability
Definition: plot2d.h:1137
void setFixedScale(double minX, double maxX, double minY, double maxY)
Set fixed scale.
Definition: plot2d.h:1049
trace & AddStaticTrace()
Add static trace.
Definition: plot2d.h:995
Single trace to be plotted.
Definition: plot2d.h:562
void color(int clr)
set color
Definition: plot2d.h:634
double value(double xfraction)
y value at fractional position along x-axis
Definition: plot2d.h:663
void set(const std::vector< double > &y)
set plot data
Definition: plot2d.h:578
void thick(int t)
set trace thickness in pixels
Definition: plot2d.h:644
void add(double x, double y)
add point to scatter trace
Definition: plot2d.h:618
int size() const
get number of points
Definition: plot2d.h:654
void clear()
clear data from trace
Definition: plot2d.h:627
void add(double y)
add new value to real time data
Definition: plot2d.h:603
A class that offers application code methods to draw on a window.
Definition: wex.h:525
void textHeight(int h)
Set text height.
Definition: wex.h:786
void text(const std::string &t, const std::vector< int > &v)
Draw text.
Definition: wex.h:727
void line(const std::vector< int > &v)
Draw line between two points.
Definition: wex.h:616
void pixel(int x, int y)
Color a pixel.
Definition: wex.h:609
void color(int r, int g, int b)
Set color for drawings.
Definition: wex.h:559
A structure containing the mouse status for event handlers.
Definition: wex.h:28