📄 trend.cc
字号:
glEnd(); // scan for all intersections size_t trX = (static_cast<size_t>(floor(intrX))); if(trX == divisions) --trX; double mul = (intrX - trX); vector<Intr> intrs; // starting position size_t i; const Value* it; if(scroll) { it = rrBuf + (trX - offset % divisions); i = trX; } else { it = rrBuf + (trX - rrBuf->count); i = 0; } if(it < rrBuf) it += divisions; if(intrFg) it += ((rrEnd - it) / divisions) * divisions; const Value* end = rrEnd - 1; for(; it < end; i += divisions, it += divisions) { size_t pos = getPosition(i, *it); // fetch the next value Value next = *(it + 1); Intr buf; if(mul < 0.5) { buf.near.value = it->value; buf.near.count = pos; } else { buf.near.value = next.value; buf.near.count = getPosition(i + 1, next); } buf.value = it->value + mul * (next.value - it->value); buf.dist = fabs(buf.value - intrY); intrs.push_back(buf); } // no intersections found if(!intrs.size()) return; // consider only the nearest n values std::sort(intrs.begin(), intrs.end()); if(intrs.size() > static_cast<size_t>(Trend::intrNum)) intrs.resize(Trend::intrNum); // draw intersections and estimate mean value double mean = 0; glBegin(GL_LINES); for(vector<Intr>::const_iterator it = intrs.begin(); it != intrs.end(); ++it) { mean += it->value; glVertex2d(0, it->value); glVertex2d(divisions, it->value); } glEnd(); mean /= intrs.size(); // switch to video coordinates glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, width, 0, height); // nearest point int nearX, nearY; project(intrs[0].near.count, intrs[0].near.value, nearX, nearY); drawCircle(nearX, nearY); if(!intrs[0].near.count) drawCircle(width, nearY); // plot values using Trend::strSpc; char buf[256]; int curY = height; sprintf(buf, "nearest: %g, mean: %g", intrs[0].near.value, mean); drawString(strSpc, curY -= Trend::fontHeight + strSpc, buf); i = 1; for(vector<Intr>::const_iterator it = intrs.begin(); it != intrs.end(); ++i, ++it) { sprintf(buf, "%d: %g", i, it->value); drawString(strSpc, curY -= Trend::fontHeight + strSpc, buf); } // restore model space glPopMatrix();}voiddrawValues(){ const Value& last = rrBuf[history - 1]; char buf[Trend::maxNumLen]; glColor3fv(textCol); sprintf(buf, "%g", loLimit); drawOSString(width, 0, buf); sprintf(buf, "%g", hiLimit); drawOSString(width, height, buf); sprintf(buf, "%g", last.value); drawLEString(buf);}voiddrawLatency(){ char buf[Trend::maxNumLen]; glColor3fv(textCol); sprintf(buf, "lat: %g", atLat.avg()); drawLEString(buf);}// redraw handlervoiddisplay(){ // reset some data lc = 0; // setup model coordinates double zero = (distrib? -(static_cast<double>(Trend::distribWidth) * divisions / (width - Trend::distribWidth)): 0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluOrtho2D(zero, divisions, loLimit, hiLimit); // background grid if(grid) drawGrid(); size_t pos = drawLine(); // other data if(distrib) drawDistrib(); if(marker) drawMarker(pos); if(intr) drawIntr(); // setup video coordinates glLoadIdentity(); gluOrtho2D(0, width, 0, height); // video stuff if(values) drawValues(); if(latency) drawLatency(); // flush buffers glutSwapBuffers(); atLat.stop();}voidremoveNANs(){ const Value* begin = rrBuf; Value* it = rrEnd; double old = NAN; while(it-- != begin) { if(isnan(it->value)) it->value = old; else if(it->value != old) old = it->value; }}voidsetLimits(){ const Value* it = rrBuf; const Value* end = rrEnd; double lo(it->value); double hi(lo); for(; it != end; ++it) { if(it->value > hi) hi = it->value; if(it->value < lo) lo = it->value; } // some vertical bounds hiLimit = hi + grSpec.y.res; loLimit = lo - grSpec.y.res;}voididle(int){ // re-register the callback glutTimerFunc(1, idle, 0); if(paused) return; // check if a redraw is really necessary bool recalc = false;#ifdef __sgi if(test_and_set((unsigned long*)(&damaged), 0)) recalc = true;#else // linux seems to support some unportable atomic stuff into asm/atomic pthread_mutex_lock(&mutex); if(damaged) { damaged = 0; recalc = true; } pthread_mutex_unlock(&mutex);#endif if(recalc) { atLat.start(); rrData->copy(rrBuf); // since we swiched from deque to rr, the size now is always fixed, and // the initial buffer is filled with NANs. We don't want NANs however, // and we don't want to handle this lone-case everywhere. if(isnan(rrBuf->value)) removeNANs(); // recalculate limits seldom if(autoLimit) setLimits(); glutPostRedisplay(); }}/* * Keyboard interaction */// Parse a grid (res+mayor)voidparseGrid(Grid& grid, char* spec){ char* p = strchr(spec, '+'); if(p) { *p++ = 0; if(*p) grid.mayor = strtoul(p, NULL, 0); } if(*spec) grid.res = strtod(spec, NULL);}// Parse a grid-specvoidparseGrSpec(GrSpec& grid, char* spec){ char* p = strchr(spec, 'x'); if(p) { *p++ = 0; if(*p) parseGrid(grid.x, p); } if(*spec) parseGrid(grid.y, spec);}voidtoggleStatus(const char* str, bool& var){ var = !var; std::cout << str << ": " << (var? "enabled": "disabled") << std::endl;}doublegetUnit(const char* str){ cout << str << "? "; double u; cin >> u; return u;}voidgetGrid(){ char buf[256]; cout << "grid-spec? "; cin.get(buf, sizeof(buf)); parseGrSpec(grSpec, buf); cin.get();}voidkeyboard(const unsigned char key, const int x, const int y){ switch(key) { case Trend::quitKey: exit(Trend::success); break; // redraw alteration case Trend::dimmedKey: toggleStatus("dimmed", dimmed); break; case Trend::distribKey: toggleStatus("distribution", distrib); break; case Trend::autolimKey: toggleStatus("autolimit", autoLimit); break; case Trend::resetlimKey: setLimits(); cout << "limits reset\n"; break; case Trend::setlimKey: loLimit = getUnit("-y"); hiLimit = getUnit("+y"); autoLimit = false; break; case Trend::smoothKey: toggleStatus("smoothing", smooth); init(); break; case Trend::scrollKey: toggleStatus("scrolling", scroll); break; case Trend::valuesKey: toggleStatus("values", values); break; case Trend::markerKey: toggleStatus("marker", marker); break; case Trend::gridKey: toggleStatus("grid", grid); break; case Trend::setResKey: getGrid(); break; case Trend::latKey: toggleStatus("latency", latency); break; case Trend::pauseKey: toggleStatus("paused", paused); default: return; } glutPostRedisplay();}voidmotion(int x, int y){ unproject(x, y, intrX, intrY); // the x position must be adjusted if(intrX < 0) intrX = 0.; else if(intrX > divisions) intrX = static_cast<double>(divisions); glutPostRedisplay();}voidmouse(int button, int state, int x, int y){ // cause a motion event internally intr = (button != GLUT_RIGHT_BUTTON); intrFg = (glutGetModifiers() & GLUT_ACTIVE_CTRL); motion(x, y);}/* * CLI and options */// Parse a hist/n, div*n, div specboolparseHistSpec(size_t& hist, size_t& div, const char* spec){ // find the separator first. // TODO: * is deprecated const char* p = strpbrk(spec, "/*x"); if((p == spec) || (p && *(p + 1) == 0)) return true; if(!p) { div = strtoul(spec, NULL, 0); hist = div + 1; return false; } else if(*p == '/') { hist = strtoul(spec, NULL, 0); div = strtoul(p + 1, NULL, 0); if(!div) return true; div = hist / div; } else { div = strtoul(spec, NULL, 0); hist = div * strtoul(p + 1, NULL, 0); } return false;}boolparseInput(Trend::input_t& input, const char* arg){ switch(arg[0]) { case 'a': input = Trend::absolute; break; case 'i': input = Trend::incremental; break; case 'd': input = Trend::differential; break; default: return true; }; return false;}boolparseFormat(Trend::format_t& format, const char* arg){ switch(arg[0]) { case 'a': format = Trend::f_ascii; break; case 'f': format = Trend::f_float; break; case 'd': format = Trend::f_double; break; case 's': format = Trend::f_short; break; case 'i': format = Trend::f_int; break; case 'l': format = Trend::f_long; break; default: return true; }; return false;}// Initialize globals through command lineintparseOptions(int argc, char* const argv[]){ // starting options memcpy(backCol, Trend::backCol, sizeof(backCol)); memcpy(textCol, Trend::textCol, sizeof(textCol)); memcpy(gridCol, Trend::gridCol, sizeof(gridCol)); memcpy(lineCol, Trend::lineCol, sizeof(lineCol)); memcpy(markCol, Trend::markCol, sizeof(markCol)); memcpy(intrCol, Trend::intrCol, sizeof(intrCol)); grSpec.x.res = grSpec.y.res = Trend::gridres; grSpec.x.mayor = grSpec.y.mayor = Trend::mayor; int arg; while((arg = getopt(argc, argv, "dDFSsvlmgG:ht:A:E:R:I:M:N:irz:f:c:")) != -1) switch(arg) { case 'd': dimmed = !dimmed; break; case 'D': distrib = !distrib; break; case 'F': fifo = !fifo; break; case 'S': smooth = !smooth; break; case 's': scroll = !scroll; break; case 'v': values = !values; break; case 'l': latency = !latency; break; case 'm': marker = !marker; break; case 'g': grid = !grid; break; case 'G': parseGrSpec(grSpec, optarg); break; case 'z': grZero = strtod(optarg, NULL); break; case 't': title = optarg; break; case 'A': parseColor(backCol, optarg); break; case 'E': parseColor(textCol, optarg); break; case 'R': parseColor(gridCol, optarg); break; case 'I': parseColor(lineCol, optarg); break; case 'M': parseColor(markCol, optarg); break; case 'N': parseColor(intrCol, optarg); break; case 'c': if(parseInput(input, optarg)) { cerr << argv[0] << ": bad input mode\n"; return -1; } break; case 'i': // TODO: deprecated input = Trend::incremental; break; case 'r': // TODO: deprecated input = Trend::differential; break; case 'f': if(parseFormat(format, optarg)) { cerr << argv[0] << ": bad format type\n"; return -1; } break; case 'h': cout << argv[0] << " usage: " << argv[0] << " [options] fifo <hist-spec|hist-sz x-div> [-y +y]\n" << argv[0] << " version: $Revision: #44 $ $Date: 2005/07/23 $\n"; return 1; default: return -1; } // main parameters argc -= optind; if(argc < 2 || argc > 5) { cerr << argv[0] << ": bad parameters\n"; return -1; } fileName = argv[optind++]; if(argc == 2 || argc == 4) { if(parseHistSpec(history, divisions, argv[optind++])) { cerr << argv[0] << ": bad hist-spec\n"; return -1; } } else { history = strtoul(argv[optind++], NULL, 0); divisions = strtoul(argv[optind++], NULL, 0); } // parameters may still be buggy if(!history || !divisions) { cerr << argv[0] << ": hist-sz or x-div can't be zero\n"; return -1; } offset = divisions - (history % divisions) + 1; // optional limiting factors if(argc == 4 || argc == 5) { loLimit = strtod(argv[optind++], NULL); hiLimit = strtod(argv[optind++], NULL); autoLimit = false; } else autoLimit = true; return 0;}void deleteFifo(){ if (unlink(fileName)) { cerr << "Error deleting fifo: " << strerror(errno) << "\n"; }}intcreateFifo(){ umask(0000); int res = mkfifo(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (res) { cerr << "Error creating fifo: " << strerror(errno) << "\n"; return -1; } if (atexit(deleteFifo)) cerr << "Could not register atexit function. Proceeding anyway\n"; return 0;}intmain(int argc, char* argv[]) try{ // parameters glutInit(&argc, argv); if(parseOptions(argc, argv)) return Trend::args; // initialize rr buffers rrData = new rr<Value>(history); rrBuf = new Value[history]; rrEnd = rrBuf + history; fillRr(NAN); if (fifo) { if (createFifo()) return Trend::fifo_error; } // start the producer thread pthread_t thrd; pthread_mutex_init(&mutex, NULL); pthread_create(&thrd, NULL, producer, argv[0]); // display, main mindow and callbacks glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutCreateWindow(title? title: argv[0]); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(motion); // first redraw init(); idle(0); // processing glutMainLoop(); return Trend::success;}catch(const std::exception& e){ std::cerr << argv[0] << ": " << e.what() << std::endl; throw;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -