Qucs-GUI
0.0.19
|
00001 /*************************************************************************** 00002 timingdiagram.cpp 00003 ------------------- 00004 begin : Sat Oct 22 2005 00005 copyright : (C) 2005 by Michael Margraf 00006 email : michael.margraf@alumni.tu-berlin.de 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00023 #include "timingdiagram.h" 00024 #include "main.h" 00025 #include "misc.h" 00026 00027 #include <cmath> 00028 #include <QPolygon> 00029 #include <QPainter> 00030 00031 00032 TimingDiagram::TimingDiagram(int _cx, int _cy) : TabDiagram(_cx, _cy) 00033 { 00034 x1 = 0; // no extension to select area 00035 y1 = 0; 00036 x2 = x3 = 300; // initial size of diagram 00037 y2 = 200; 00038 Name = "Time"; 00039 xAxis.limit_min = 0.0; // scroll bar position (needs to be saved in file) 00040 00041 calcDiagram(); 00042 } 00043 00044 TimingDiagram::~TimingDiagram() 00045 { 00046 } 00047 00048 // ------------------------------------------------------------ 00049 void TimingDiagram::paint(ViewPainter *p) 00050 { 00051 // paint all lines 00052 foreach(Line *pl, Lines) { 00053 p->Painter->setPen(pl->style); 00054 p->drawLine(cx+pl->x1, cy-pl->y1, cx+pl->x2, cy-pl->y2); 00055 } 00056 00057 p->Painter->setPen(Qt::black); 00058 // write whole text 00059 foreach(Text *pt, Texts) 00060 p->drawText(pt->s, cx+pt->x, cy-pt->y); 00061 00062 00063 if(y1 > 0) { // paint scroll bar ? 00064 int x, y, dx, dy; 00065 QPolygon Points; 00066 // draw scroll bar 00067 p->fillRect(cx+yAxis.numGraphs, cy+2, zAxis.numGraphs, 14, QColor(192, 192, 192)); 00068 00069 int bx = cx+yAxis.numGraphs+zAxis.numGraphs; 00070 // draw frame for scroll bar 00071 p->Painter->setPen(QPen(Qt::black,0)); 00072 p->drawLine(cx+xAxis.numGraphs, cy, cx+xAxis.numGraphs, cy+17); 00073 p->drawLine(cx+xAxis.numGraphs+17, cy, cx+xAxis.numGraphs+17, cy+17); 00074 p->drawLine(cx+xAxis.numGraphs, cy+17, cx+x2, cy+17); 00075 p->drawLine(cx+x2, cy, cx+x2, cy+17); 00076 p->drawLine(cx+x2-17, cy, cx+x2-17, cy+17); 00077 00078 // draw the arrows above and below the scroll bar 00079 p->Painter->setBrush(QColor(192, 192, 192)); 00080 p->Painter->setPen(QColor(152, 152, 152)); 00081 p->drawLine(cx+yAxis.numGraphs, cy+15, bx, cy+15); 00082 p->drawLine(bx, cy+2, bx, cy+15); 00083 00084 p->map(cx+xAxis.numGraphs+3, cy+3, x, y); 00085 p->map(cx+xAxis.numGraphs+14, cy+14, dx, dy); 00086 Points.setPoints(3, x, (y+dy)>>1, dx, y, dx, dy); 00087 p->Painter->drawConvexPolygon(Points); 00088 p->Painter->setPen(QColor(224, 224, 224)); 00089 p->Painter->drawLine(x, (y+dy)>>1, dx, y); 00090 p->drawLine(cx+yAxis.numGraphs, cy+2, bx, cy+2); 00091 p->drawLine(cx+yAxis.numGraphs, cy+2, cx+yAxis.numGraphs, cy+15); 00092 00093 p->Painter->setPen(QColor(152, 152, 152)); 00094 dx -= x; 00095 p->map(cx+x2-3, cy+3, x, y); 00096 Points.setPoints(3, x, (y+dy)>>1, x-dx, y, x-dx, dy); 00097 p->Painter->drawConvexPolygon(Points); 00098 p->Painter->setPen(QColor(208, 208, 208)); 00099 p->Painter->drawLine(x-dx, y, x, (y+dy)>>1); 00100 p->Painter->setPen(QColor(224, 224, 224)); 00101 p->Painter->drawLine(x-dx, y, x-dx, dy); 00102 00103 p->Painter->setBrush(QBrush(Qt::NoBrush)); 00104 } 00105 00106 00107 if(isSelected) { 00108 p->Painter->setPen(QPen(Qt::darkGray,3)); 00109 p->drawRect(cx-5, cy-y2-5, x2+10, y2+10); 00110 p->Painter->setPen(QPen(Qt::darkRed,2)); 00111 p->drawResizeRect(cx, cy-y2); // markers for changing the size 00112 p->drawResizeRect(cx, cy); 00113 p->drawResizeRect(cx+x2, cy-y2); 00114 p->drawResizeRect(cx+x2, cy); 00115 } 00116 } 00117 00118 // ------------------------------------------------------------ 00119 int TimingDiagram::calcDiagram() 00120 { 00121 Lines.clear(); 00122 Texts.clear(); 00123 00124 y1 = 0; // no scroll bar 00125 x3 = x2; 00126 // get size of text using the screen-compatible metric 00127 QFontMetrics metrics(QucsSettings.font, 0); 00128 int tHeight = metrics.lineSpacing(); 00129 QString Str; 00130 int colWidth=0, x=4, y, xStart = 0, z; 00131 00132 int NumAll=0; // how many values per row 00133 int NumLeft=0; // how many values could not be written 00134 int invisibleCount = 0; // how many values are invisible 00135 00136 if(y2 < (tHeight + 8)) 00137 y2 = tHeight + 8; 00138 y = y2 - tHeight - 6; 00139 00140 // outer frame 00141 Lines.append(new Line(0, y2, x2, y2, QPen(Qt::black,0))); 00142 Lines.append(new Line(0, y2, 0, 0, QPen(Qt::black,0))); 00143 Lines.append(new Line(x2, y2, x2, 0, QPen(Qt::black,0))); 00144 Lines.append(new Line(0, 0, x2, 0, QPen(Qt::black,0))); 00145 Lines.append(new Line(0, y+2, x2, y+2, QPen(Qt::black,0))); 00146 00147 if(xAxis.limit_min < 0.0) 00148 xAxis.limit_min = 0.0; 00149 00150 Graph *firstGraph; 00151 00152 QListIterator<Graph *> ig(Graphs); 00153 Graph *g = 0; 00154 if (ig.hasNext()) // point to first graph 00155 g = ig.next(); 00156 00157 if(g == 0) { // no variables specified in diagram ? 00158 Str = QObject::tr("no variables"); 00159 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y2); 00160 if(colWidth >= 0) 00161 Texts.append(new Text(x, y2-2, Str)); // independent variable 00162 return 0; 00163 } 00164 00165 00166 double *px; 00167 // any graph with data ? 00168 while(g->isEmpty()) { 00169 if (!ig.hasNext()) break; // no more graphs, exit loop 00170 g = ig.next(); // point to next graph 00171 } 00172 00173 if(g->isEmpty()) { // no graph with data found ? 00174 Str = QObject::tr("no data"); 00175 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y2); 00176 if(colWidth < 0) return 0; 00177 Texts.append(new Text(x, y2-2, Str)); 00178 return 0; 00179 } 00180 firstGraph = g; 00181 00182 00183 // First check the maximum bit number of all vectors. 00184 colWidth = 0; 00185 foreach(Graph *g, Graphs) 00186 if(g->cPointsY) { 00187 if(g->Var.right(2) == ".X") { 00188 z = strlen((char*)g->cPointsY); 00189 if(z > colWidth) 00190 colWidth = z; 00191 } 00192 else { 00193 z = 8; 00194 if(z > colWidth) 00195 colWidth = z; 00196 } 00197 } 00198 int TimeStepWidth = colWidth * metrics.width("X") + 8; 00199 if(TimeStepWidth < 40) 00200 TimeStepWidth = 40; 00201 00202 00203 colWidth = 0; 00204 if(!firstGraph->isEmpty()) { 00205 // ................................................ 00206 if(firstGraph->numAxes() > 1) { 00207 Str = QObject::tr("wrong dependency"); 00208 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y2); 00209 if(colWidth >= 0) 00210 Texts.append(new Text(x, y2-2, Str)); // independent variable 00211 return 0; 00212 } 00213 00214 00215 // first, write name of independent variable 00216 DataX const *pD = firstGraph->axis(0); 00217 NumAll = pD->count; 00218 Str = pD->Var; 00219 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y2); 00220 if(colWidth < 0) return 1; 00221 Texts.append(new Text(x, y2-2, Str)); 00222 00223 00224 y -= 5; 00225 // write all dependent variable names to get width of first column 00226 foreach(Graph *g, Graphs) { 00227 if(y < tHeight) break; 00228 Str = g->Var; 00229 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y); 00230 if(colWidth < 0) return 1; 00231 Texts.append(new Text(x, y, Str)); // dependent variable 00232 y -= tHeight + 2; 00233 } 00234 x += colWidth + 13; 00235 xAxis.numGraphs = x -6; 00236 Lines.append(new Line(x-6, y2, x-6, 0, QPen(Qt::black,0))); 00237 xStart = x; 00238 00239 00240 invisibleCount = NumAll - (x2-xAxis.numGraphs)/TimeStepWidth; 00241 if(invisibleCount <= 0) xAxis.limit_min = 0.0; // longer than needed 00242 else { 00243 NumLeft = invisibleCount - int(xAxis.limit_min + 0.5); 00244 if(invisibleCount < int(xAxis.limit_min + 0.5)) 00245 xAxis.limit_min = double(invisibleCount); // adjust limit of scroll bar 00246 } 00247 00248 00249 // write independent variable values (usually time) 00250 y = y2-tHeight-4; 00251 px = pD->Points; 00252 z = int(xAxis.limit_min + 0.5); 00253 px += z; 00254 z = pD->count - z; 00255 for( ; z>0; z--) { 00256 Str = misc::num2str(*(px++)); 00257 colWidth = metrics.width(Str); // width of text 00258 if(x+colWidth+2 >= x2) break; 00259 00260 Texts.append(new Text( x, y2-2, Str)); 00261 Lines.append(new Line(x+5, y, x+5, y-3, QPen(Qt::black,0))); 00262 x += TimeStepWidth; 00263 } 00264 00265 } // of "if no data in graphs" 00266 00267 00268 tHeight += 2; 00269 // ................................................ 00270 // work on all dependent variables 00271 QPen Pen; 00272 int yLast, yNow; 00273 y = y2-tHeight-9; 00274 foreach(Graph *g, Graphs) { 00275 if(y < tHeight) { 00276 // mark lack of space with a small arrow 00277 Lines.append(new Line(4, 6, 4, -7, QPen(Qt::red,2))); 00278 Lines.append(new Line(1, 0, 4, -7, QPen(Qt::red,2))); 00279 Lines.append(new Line(7, 0, 4, -7, QPen(Qt::red,2))); 00280 break; 00281 } 00282 00283 x = xStart + 5; 00284 colWidth = 0; 00285 00286 if(g->cPointsY == 0) { 00287 Str = QObject::tr("no data"); 00288 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y); 00289 if(colWidth < 0) goto funcEnd; 00290 Texts.append(new Text(x, y, Str)); 00291 y -= tHeight; 00292 continue; 00293 } 00294 00295 if(!sameDependencies(g, firstGraph)) { 00296 Str = QObject::tr("wrong dependency"); 00297 colWidth = checkColumnWidth(Str, metrics, colWidth, x, y); 00298 if(colWidth < 0) goto funcEnd; 00299 Texts.append(new Text(x, y, Str)); 00300 y -= tHeight; 00301 continue; 00302 } 00303 00304 Pen = QPen(g->Color, g->Thick); // default is solid line 00305 switch(g->Style) { 00306 case 1: Pen.setStyle(Qt::DashLine); break; 00307 case 2: Pen.setStyle(Qt::DotLine); break; 00308 default: break; 00309 } 00310 00311 z = int(xAxis.limit_min + 0.5); 00312 if(g->Var.right(2) != ".X") { // not digital variable ? 00313 px = g->cPointsY; 00314 px += 2 * z; 00315 z = g->axis(0)->count - z; 00316 yNow = 1 + ((tHeight - 6) >> 1); 00317 Lines.append(new Line(x, y-yNow, x+2, y-1, Pen)); 00318 Lines.append(new Line(x+2, y-tHeight+5, x, y-yNow, Pen)); 00319 for( ; z>0; z--) { 00320 if(x+TimeStepWidth >= x2) break; 00321 Lines.append(new Line(x+2, y-1, x+TimeStepWidth-2, y-1, Pen)); 00322 Lines.append(new Line(x+2, y-tHeight+5, x+TimeStepWidth-2, y-tHeight+5, Pen)); 00323 00324 if (*(px+1) == 0.0) 00325 // output real number 00326 Texts.append(new Text(x+3, y,QString::number(*px))); 00327 else 00328 // output magnitude of (complex) number 00329 Texts.append(new Text(x+3, y, 00330 QString::number(sqrt((*px)*(*px) + (*(px+1))*(*(px+1)))))); 00331 00332 px += 2; 00333 x += TimeStepWidth; 00334 Lines.append(new Line(x-2, y-tHeight+5, x+2, y-1, Pen)); 00335 Lines.append(new Line(x+2, y-tHeight+5, x-2, y-1, Pen)); 00336 } 00337 y -= tHeight; 00338 continue; 00339 } 00340 00341 00342 // digital variable !!! 00343 char *pcx = (char*)g->cPointsY; 00344 pcx += z * (strlen(pcx)+1); 00345 00346 if(strlen(pcx) < 2) { // vector or single bit ? 00347 00348 // It is single "bit". 00349 yLast = 0; 00350 if(z > 0) yLast += 2; // vertical line before first value ? 00351 switch(*(pcx-yLast)) { // high or low ? 00352 case '0': // low 00353 yLast = tHeight - 5; 00354 break; 00355 case '1': // high 00356 yLast = 1; 00357 break; 00358 default: 00359 yLast = 1 + ((tHeight - 6) >> 1); 00360 } 00361 00362 z = g->axis(0)->count - z; 00363 for( ; z>0; z--) { 00364 00365 switch(*pcx) { 00366 case '0': // low 00367 yNow = tHeight - 5; 00368 break; 00369 case '1': // high 00370 yNow = 1; 00371 break; 00372 default: 00373 yNow = 1 + ((tHeight - 6) >> 1); 00374 } 00375 00376 if(yLast != yNow) 00377 Lines.append(new Line(x, y-yLast, x, y-yNow, Pen)); 00378 if(x+TimeStepWidth >= x2) break; 00379 if((*pcx & 254) == '0') 00380 Lines.append(new Line(x, y-yNow, x+TimeStepWidth, y-yNow, Pen)); 00381 else { 00382 Texts.append(new Text(x+(TimeStepWidth>>1)-3, y, QString(pcx))); 00383 Lines.append(new Line(x+3, y-1, x+TimeStepWidth-3, y-1, Pen)); 00384 Lines.append(new Line(x+3, y-tHeight+5, x+TimeStepWidth-3, y-tHeight+5, Pen)); 00385 Lines.append(new Line(x, y-yNow, x+3, y-1, Pen)); 00386 Lines.append(new Line(x, y-yNow, x+3, y-tHeight+5, Pen)); 00387 Lines.append(new Line(x+TimeStepWidth-3, y-1, x+TimeStepWidth, y-yNow, Pen)); 00388 Lines.append(new Line(x+TimeStepWidth-3, y-tHeight+5, x+TimeStepWidth, y-yNow, Pen)); 00389 } 00390 00391 yLast = yNow; 00392 x += TimeStepWidth; 00393 pcx += 2; 00394 } 00395 00396 } 00397 else { // It is a bit vector !!! 00398 00399 z = g->axis(0)->count - z; 00400 yNow = 1 + ((tHeight - 6) >> 1); 00401 Lines.append(new Line(x, y-yNow, x+2, y-1, Pen)); 00402 Lines.append(new Line(x+2, y-tHeight+5, x, y-yNow, Pen)); 00403 for( ; z>0; z--) { 00404 if(x+TimeStepWidth >= x2) break; 00405 Lines.append(new Line(x+2, y-1, x+TimeStepWidth-2, y-1, Pen)); 00406 Lines.append(new Line(x+2, y-tHeight+5, x+TimeStepWidth-2, y-tHeight+5, Pen)); 00407 00408 Texts.append(new Text(x+3, y, QString(pcx))); 00409 00410 x += TimeStepWidth; 00411 pcx += strlen(pcx) + 1; 00412 Lines.append(new Line(x-2, y-tHeight+5, x+2, y-1, Pen)); 00413 Lines.append(new Line(x+2, y-tHeight+5, x-2, y-1, Pen)); 00414 } 00415 } 00416 00417 y -= tHeight; 00418 } // of "for(Graphs...)" 00419 00420 00421 funcEnd: 00422 if(invisibleCount > 0) { // could all values be displayed ? 00423 x = x2 - xAxis.numGraphs - 37; 00424 if(x < MIN_SCROLLBAR_SIZE+2) { // not enough space for scrollbar ? 00425 Lines.append(new Line(x2, 0, x2, -17, QPen(Qt::red,0))); 00426 Lines.append(new Line(xAxis.numGraphs, -17, x2, -17, QPen(Qt::red,0))); 00427 Lines.append(new Line(xAxis.numGraphs, 0, xAxis.numGraphs, -17, QPen(Qt::red,0))); 00428 return 1; 00429 } 00430 00431 y1 = 18; // extend the select area to the bottom 00432 z = int(xAxis.limit_min + 0.5); 00433 if(NumLeft < 0) NumLeft = 0; 00434 y = NumAll - NumLeft - z; 00435 00436 // number of data (times) 00437 zAxis.limit_max = double(firstGraph->axis(0)->count); 00438 00439 // position of scroll bar in pixel 00440 yAxis.numGraphs = x * z / NumAll + xAxis.numGraphs + 19; 00441 00442 // length of scroll bar 00443 zAxis.numGraphs = x * y / NumAll; 00444 if(zAxis.numGraphs < MIN_SCROLLBAR_SIZE) { 00445 yAxis.numGraphs -= (MIN_SCROLLBAR_SIZE - zAxis.numGraphs + 1) 00446 * z / NumAll; 00447 zAxis.numGraphs = MIN_SCROLLBAR_SIZE; 00448 00449 x = x2 - 19 - yAxis.numGraphs - zAxis.numGraphs; 00450 if(x < 0) yAxis.numGraphs += x; 00451 } 00452 00453 xAxis.limit_max = double(y); 00454 } 00455 00456 return 1; 00457 } 00458 00459 // ------------------------------------------------------------ 00460 int TimingDiagram::scroll(int clickPos) 00461 { 00462 if(y1 <= 0) return 0; // no scroll bar ? 00463 int tmp = int(xAxis.limit_min + 0.5); 00464 00465 int x = cx; 00466 if(clickPos > (cx+x2-20)) { // scroll one value to the right ? 00467 xAxis.limit_min++; 00468 } 00469 else { 00470 x += xAxis.numGraphs + 20; 00471 if(clickPos < x) { // scroll bar one value to the left ? 00472 if(xAxis.limit_min <= 0.0) return 0; 00473 xAxis.limit_min--; 00474 } 00475 else { 00476 x = cx + yAxis.numGraphs; 00477 if(clickPos < x) // scroll bar one page to the left ? 00478 xAxis.limit_min -= xAxis.limit_max; 00479 else { 00480 x += zAxis.numGraphs; 00481 if(clickPos > x) // one page to the right ? 00482 xAxis.limit_min += xAxis.limit_max; 00483 else 00484 return 2; // click on position bar 00485 } 00486 } 00487 } 00488 00489 calcDiagram(); 00490 if(tmp == int(xAxis.limit_min+0.5)) 00491 return 0; // did anything change ? 00492 00493 return 1; 00494 } 00495 00496 // ------------------------------------------------------------ 00497 bool TimingDiagram::scrollTo(int initial, int dx, int) 00498 { 00499 int tmp = int(xAxis.limit_min + 0.5); 00500 xAxis.limit_min = double(initial); 00501 xAxis.limit_min += double(dx) / double(x2-xAxis.numGraphs-39) * zAxis.limit_max; 00502 xAxis.limit_min = floor(xAxis.limit_min + 0.5); 00503 00504 calcDiagram(); 00505 if(tmp == int(xAxis.limit_min + 0.5)) 00506 return false; // did anything change ? 00507 00508 return true; 00509 } 00510 00511 // ------------------------------------------------------------ 00512 Diagram* TimingDiagram::newOne() 00513 { 00514 return new TimingDiagram(); 00515 } 00516 00517 // ------------------------------------------------------------ 00518 Element* TimingDiagram::info(QString& Name, char* &BitmapFile, bool getNewOne) 00519 { 00520 Name = QObject::tr("Timing Diagram"); 00521 BitmapFile = (char *) "timing"; 00522 00523 if(getNewOne) return new TimingDiagram(); 00524 return 0; 00525 }