Qucs-GUI
0.0.19
|
00001 /*************************************************************************** 00002 diagram.cpp 00003 ------------- 00004 begin : Thu Oct 2 2003 00005 copyright : (C) 2003, 2004, 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 00027 #if HAVE_CONFIG_H 00028 # include <config.h> 00029 #endif 00030 #include <stdlib.h> 00031 #include <cmath> 00032 #include <float.h> 00033 #if HAVE_IEEEFP_H 00034 # include <ieeefp.h> 00035 #endif 00036 #include <locale.h> 00037 00038 #include "diagram.h" 00039 #include "main.h" 00040 #include "mnemo.h" 00041 #include "schematic.h" 00042 00043 #include "rect3ddiagram.h" 00044 #include "misc.h" 00045 00046 #include <QTextStream> 00047 #include <QMessageBox> 00048 #include <QRegExp> 00049 #include <QDateTime> 00050 #include <QPainter> 00051 #include <QDebug> 00052 00053 Diagram::Diagram(int _cx, int _cy) 00054 { 00055 cx = _cx; cy = _cy; 00056 00057 // x1, x2, y1, y2 are the selectable boundings of the diagram, but these 00058 // are the real boundings. They are set in "createAxisLabels()". 00059 Bounding_x1 = Bounding_x2 = Bounding_y1 = Bounding_y2 = 0; 00060 00061 xAxis.numGraphs = yAxis.numGraphs = zAxis.numGraphs = 0; 00062 xAxis.min = xAxis.low = 00063 yAxis.min = yAxis.low = zAxis.min = zAxis.low = 0.0; 00064 xAxis.max = xAxis.up = 00065 yAxis.max = yAxis.up = zAxis.max = zAxis.up = 1.0; 00066 xAxis.GridOn = yAxis.GridOn = true; 00067 zAxis.GridOn = false; 00068 xAxis.log = yAxis.log = zAxis.log = false; 00069 00070 xAxis.limit_min = yAxis.limit_min = zAxis.limit_min = 0.0; 00071 xAxis.limit_max = yAxis.limit_max = zAxis.limit_max = 1.0; 00072 xAxis.step = yAxis.step = zAxis.step = 1.0; 00073 xAxis.autoScale = yAxis.autoScale = zAxis.autoScale = true; 00074 00075 rotX = 315; // for 3D diagram 00076 rotY = 0; 00077 rotZ = 225; 00078 hideLines = true; // hide invisible lines 00079 00080 Type = isDiagram; 00081 isSelected = false; 00082 GridPen = QPen(Qt::lightGray,0); 00083 } 00084 00085 Diagram::~Diagram() 00086 { 00087 } 00088 00092 void Diagram::paint(ViewPainter *p) 00093 { 00094 // paint all lines 00095 foreach(Line *pl, Lines) { 00096 p->Painter->setPen(pl->style); 00097 p->drawLine(cx+pl->x1, cy-pl->y1, cx+pl->x2, cy-pl->y2); 00098 } 00099 00100 // paint all arcs (1 pixel larger to compensate for strange circle method) 00101 foreach(Arc *pa, Arcs) { 00102 p->Painter->setPen(pa->style); 00103 p->drawArc(cx+pa->x, cy-pa->y, pa->w, pa->h, pa->angle, pa->arclen); 00104 } 00105 00106 // draw all graphs 00107 foreach(Graph *pg, Graphs) 00108 pg->paint(p, cx, cy); 00109 00110 // keep track of painter state 00111 p->Painter->save(); 00112 00113 // write whole text (axis label inclusively) 00114 QMatrix wm = p->Painter->worldMatrix(); 00115 foreach(Text *pt, Texts) { 00116 p->Painter->setWorldMatrix( 00117 QMatrix(pt->mCos, -pt->mSin, pt->mSin, pt->mCos, 00118 p->DX + float(cx+pt->x) * p->Scale, 00119 p->DY + float(cy-pt->y) * p->Scale)); 00120 00121 p->Painter->setPen(pt->Color); 00122 p->Painter->drawText(0, 0, pt->s); 00123 } 00124 p->Painter->setWorldMatrix(wm); 00125 p->Painter->setWorldMatrixEnabled(false); 00126 00127 // restore painter state 00128 p->Painter->restore(); 00129 00130 // draw markers last, so they are at the top of painting layers 00131 foreach(Graph *pg, Graphs) 00132 foreach(Marker *pm, pg->Markers) 00133 pm->paint(p, cx, cy); 00134 00135 00136 if(isSelected) { 00137 int x_, y_; 00138 float fx_, fy_; 00139 p->map(cx, cy-y2, x_, y_); 00140 fx_ = float(x2)*p->Scale + 10; 00141 fy_ = float(y2)*p->Scale + 10; 00142 00143 p->Painter->setPen(QPen(Qt::darkGray,3)); 00144 p->Painter->drawRect(x_-5, y_-5, TO_INT(fx_), TO_INT(fy_)); 00145 p->Painter->setPen(QPen(Qt::darkRed,2)); 00146 p->drawResizeRect(cx, cy-y2); // markers for changing the size 00147 p->drawResizeRect(cx, cy); 00148 p->drawResizeRect(cx+x2, cy-y2); 00149 p->drawResizeRect(cx+x2, cy); 00150 } 00151 } 00152 00153 // ------------------------------------------------------------ 00154 void Diagram::paintScheme(Schematic *p) 00155 { 00156 p->PostPaintEvent(_Rect, cx, cy-y2, x2, y2); 00157 } 00158 00162 void Diagram::createAxisLabels() 00163 { 00164 int x, y, w, wmax = 0; 00165 QString Str; 00166 // get size of text using the screen-compatible metric 00167 QFontMetrics metrics(QucsSettings.font, 0); 00168 int LineSpacing = metrics.lineSpacing(); 00169 00170 00171 x = (x2>>1); 00172 y = -y1; 00173 if(xAxis.Label.isEmpty()) { 00174 // write all x labels ---------------------------------------- 00175 foreach(Graph *pg, Graphs) { 00176 DataX const *pD = pg->axis(0); 00177 if(!pD) continue; 00178 y -= LineSpacing; 00179 if(Name[0] != 'C') { // locus curve ? 00180 w = metrics.width(pD->Var) >> 1; 00181 if(w > wmax) wmax = w; 00182 Texts.append(new Text(x-w, y, pD->Var, pg->Color, 12.0)); 00183 } 00184 else { 00185 w = metrics.width("real("+pg->Var+")") >> 1; 00186 if(w > wmax) wmax = w; 00187 Texts.append(new Text(x-w, y, "real("+pg->Var+")", 00188 pg->Color, 12.0)); 00189 } 00190 } 00191 } 00192 else { 00193 y -= LineSpacing; 00194 encode_String(xAxis.Label, Str); 00195 w = metrics.width(Str) >> 1; 00196 if(w > wmax) wmax = w; 00197 Texts.append(new Text(x-w, y, Str, Qt::black, 12.0)); 00198 } 00199 Bounding_y2 = 0; 00200 Bounding_y1 = y - LineSpacing; 00201 Bounding_x2 = wmax - (x2 >> 1); 00202 if(Bounding_x2 < 0) Bounding_x2 = 0; 00203 Bounding_x1 = Bounding_x2; 00204 00205 00206 wmax = 0; 00207 x = -x1; 00208 y = y2>>1; 00209 if(yAxis.Label.isEmpty()) { 00210 // draw left y-label for all graphs ------------------------------ 00211 foreach(Graph *pg, Graphs) { 00212 if(pg->yAxisNo != 0) continue; 00213 if(pg->cPointsY) { 00214 if(Name[0] != 'C') { // location curve ? 00215 w = metrics.width(pg->Var) >> 1; 00216 if(w > wmax) wmax = w; 00217 Texts.append(new Text(x, y-w, pg->Var, pg->Color, 12.0, 0.0, 1.0)); 00218 } 00219 else { 00220 w = metrics.width("imag("+pg->Var+")") >> 1; 00221 if(w > wmax) wmax = w; 00222 Texts.append(new Text(x, y-w, "imag("+pg->Var+")", 00223 pg->Color, 12.0, 0.0, 1.0)); 00224 } 00225 } 00226 else { // if no data => <invalid> 00227 w = metrics.width(pg->Var+INVALID_STR) >> 1; 00228 if(w > wmax) wmax = w; 00229 Texts.append(new Text(x, y-w, pg->Var+INVALID_STR, 00230 pg->Color, 12.0, 0.0, 1.0)); 00231 } 00232 x -= LineSpacing; 00233 } 00234 } 00235 else { 00236 encode_String(yAxis.Label, Str); 00237 w = metrics.width(Str) >> 1; 00238 if(w > wmax) wmax = w; 00239 Texts.append(new Text(x, y-w, Str, Qt::black, 12.0, 0.0, 1.0)); 00240 x -= LineSpacing; 00241 } 00242 if(Bounding_x1 < -x) Bounding_x1 = -x; 00243 00244 00245 x = x3; 00246 y = y2>>1; 00247 if(zAxis.Label.isEmpty()) { 00248 // draw right y-label for all graphs ------------------------------ 00249 foreach(Graph *pg, Graphs) { 00250 if(pg->yAxisNo != 1) continue; 00251 if(pg->cPointsY) { 00252 if(Name[0] != 'C') { // location curve ? 00253 w = metrics.width(pg->Var) >> 1; 00254 if(w > wmax) wmax = w; 00255 Texts.append(new Text(x, y+w, pg->Var, 00256 pg->Color, 12.0, 0.0, -1.0)); 00257 } 00258 else { 00259 w = metrics.width("imag("+pg->Var+")") >> 1; 00260 if(w > wmax) wmax = w; 00261 Texts.append(new Text(x, y+w, "imag("+pg->Var+")", 00262 pg->Color, 12.0, 0.0, -1.0)); 00263 } 00264 } 00265 else { // if no data => <invalid> 00266 w = metrics.width(pg->Var+INVALID_STR) >> 1; 00267 if(w > wmax) wmax = w; 00268 Texts.append(new Text(x, y+w, pg->Var+INVALID_STR, 00269 pg->Color, 12.0, 0.0, -1.0)); 00270 } 00271 x += LineSpacing; 00272 } 00273 } 00274 else { 00275 encode_String(zAxis.Label, Str); 00276 w = metrics.width(Str) >> 1; 00277 if(w > wmax) wmax = w; 00278 Texts.append(new Text(x, y+w, Str, Qt::black, 12.0, 0.0, -1.0)); 00279 } 00280 x -= x2; 00281 if(Bounding_x2 < x) Bounding_x2 = x; 00282 00283 wmax -= y2 >> 1; 00284 if(wmax > 0) { 00285 Bounding_y2 = wmax; 00286 wmax *= -1; 00287 if(wmax < Bounding_y1) Bounding_y1 = wmax; 00288 } 00289 } 00290 00291 // ------------------------------------------------------------ 00292 int Diagram::regionCode(float x, float y) const 00293 { 00294 int code=0; // code for clipping 00295 if(x < 0.0) 00296 code |= 1; 00297 else if(x > float(x2)) // compare as float to avoid integer overflow 00298 code |= 2; 00299 00300 if(y < 0.0) 00301 code |= 4; 00302 else if(y > float(y2)) // compare as float to avoid integer overflow 00303 code |= 8; 00304 00305 return code; 00306 } 00307 00308 // ------------------------------------------------------------ 00309 // Is virtual. This one is for round diagrams only. 00310 bool Diagram::insideDiagram(float x, float y) const 00311 { 00312 float R = float(x2)/2.0 + 1.0; // +1 seems better (graph sometimes little outside) 00313 x -= R; 00314 y -= R; 00315 return ((x*x + y*y) <= R*R); 00316 } 00317 00321 Marker* Diagram::setMarker(int x, int y) 00322 { 00323 if(getSelected(x, y)) { 00324 // test all graphs of the diagram 00325 foreach(Graph *pg,Graphs) { 00326 int n = pg->getSelected(x-cx, cy-y); // sic! 00327 if(n >= 0) { 00328 assert(pg->parentDiagram() == this); 00329 Marker *pm = new Marker(pg, n, x-cx, y-cy); 00330 pg->Markers.append(pm); 00331 return pm; 00332 } 00333 } 00334 } 00335 return NULL; 00336 } 00337 00341 void Diagram::rectClip(Graph::iterator &p) const 00342 { 00343 int code, z=0; 00344 float x=0, y=0, dx, dy; 00345 float x_1 = (p-2)->getScrX(), y_1 = (p-2)->getScrY(); 00346 float x_2 = (p-1)->getScrX(), y_2 = (p-1)->getScrY(); 00347 00348 int code1 = regionCode(x_1, y_1); 00349 int code2 = regionCode(x_2, y_2); 00350 if((code1 | code2) == 0) return; // line completly inside ? 00351 00352 if(code1 != 0) if((p-3)->isPt()) { 00353 p++; 00354 (p-3)->setStrokeEnd(); 00355 } 00356 if(code1 & code2) // line not visible at all ? 00357 goto endWithHidden; 00358 00359 if(code2 != 0) { 00360 p->setStrokeEnd(); 00361 (p+1)->setScr(x_2, y_2); 00362 z += 2; 00363 } 00364 00365 00366 for(;;) { 00367 if((code1 | code2) == 0) break; // line completly inside ? 00368 00369 if(code1) code = code1; 00370 else code = code2; 00371 00372 dx = x_2 - x_1; // dx and dy never equals zero ! 00373 dy = y_2 - y_1; 00374 if(code & 1) { 00375 y = y_1 - dy * x_1 / dx; 00376 x = 0.0; 00377 } 00378 else if(code & 2) { 00379 y = y_1 + dy * (x2-x_1) / dx; 00380 x = float(x2); 00381 } 00382 else if(code & 4) { 00383 x = x_1 - dx * y_1 / dy; 00384 y = 0.0; 00385 } 00386 else if(code & 8) { 00387 x = x_1 + dx * (y2-y_1) / dy; 00388 y = float(y2); 00389 } 00390 00391 if(code == code1) { 00392 x_1 = x; 00393 y_1 = y; 00394 code1 = regionCode(x, y); 00395 } 00396 else { 00397 x_2 = x; 00398 y_2 = y; 00399 code2 = regionCode(x, y); 00400 } 00401 if(code1 & code2) 00402 goto endWithHidden; // line not visible at all ? 00403 } 00404 00405 (p-2)->setScr(x_1, y_1); 00406 (p-1)->setScr(x_2, y_2); 00407 p += z; 00408 return; 00409 00410 endWithHidden: 00411 (p-2)->setScr(x_2, y_2); 00412 p -= 1; 00413 } 00414 00418 void Diagram::clip(Graph::iterator &p) const 00419 { 00420 float R = float(x2) / 2.0; 00421 float x_1 = (p-2)->getScrX() - R, y_1 = (p-2)->getScrY() - R; 00422 float x_2 = (p-1)->getScrX() - R, y_2 = (p-1)->getScrY() - R; 00423 00424 float dt1 = R*R; // square of radius 00425 float dt2 = dt1 - x_2*x_2 - y_2*y_2; 00426 dt1 -= x_1*x_1 + y_1*y_1; 00427 00428 if(dt1 >= 0.0) if(dt2 >= 0.0) return; // line completly inside ? 00429 00430 if(dt1 < 0.0) if((p-3)->isPt()) { // is there already a line end flag ? 00431 p++; 00432 (p-3)->setStrokeEnd(); 00433 } 00434 00435 float x = x_1-x_2; 00436 float y = y_1-y_2; 00437 float C = x_1*x + y_1*y; 00438 float D = x*x + y*y; 00439 float F = C*C + dt1*D; 00440 00441 x_1 += R; 00442 y_1 += R; 00443 x_2 += R; 00444 y_2 += R; 00445 if(F <= 0.0) { // line not visible at all ? 00446 (p-2)->setScr(x_2, y_2); 00447 p -= 1; 00448 return; 00449 } 00450 00451 int code = 0; 00452 R = sqrt(F); 00453 dt1 = C - R; 00454 if((dt1 > 0.0) && (dt1 < D)) { // intersection outside start/end point ? 00455 (p-2)->setScr(x_1 - x*dt1 / D, y_1 - y*dt1 / D); 00456 code |= 1; 00457 } 00458 else { 00459 (p-2)->setScr(x_1, y_1); 00460 } 00461 00462 dt2 = C + R; 00463 if((dt2 > 0.0) && (dt2 < D)) { // intersection outside start/end point ? 00464 (p-1)->setScr(x_1 - x*dt2 / D, y_1 - y*dt2 / D); 00465 p->setStrokeEnd(); 00466 p += 2; 00467 code |= 2; 00468 } 00469 (p-1)->setScr(x_2, y_2); 00470 00471 if(code == 0) { // intersections both lie outside ? 00472 (p-2)->setScr(x_2, y_2); 00473 --p; 00474 } 00475 00476 } 00477 00478 00479 // ------------------------------------------------------------ 00480 // g->Points must already be empty!!! 00481 // is this a Graph Member? 00482 void Diagram::calcData(Graph *g) 00483 { 00484 double *px; 00485 double *pz = g->cPointsY; 00486 if(!pz) return; 00487 if(g->numAxes() < 1) return; 00488 00489 int i, z, Counter=2; 00490 int Size = ((2*(g->count(0)) + 1) * g->countY) + 10; 00491 00492 if(xAxis.autoScale) if(yAxis.autoScale) if(zAxis.autoScale) 00493 Counter = -50000; 00494 00495 double Dummy = 0.0; // not used 00496 double *py = &Dummy; 00497 00498 g->resizeScrPoints(Size); 00499 auto p = g->begin(); 00500 auto p_end = g->begin(); 00501 p_end += Size - 9; // limit of buffer 00502 p->setStrokeEnd(); 00503 ++p; 00504 assert(p!=g->end()); 00505 00506 Axis *pa; 00507 if(g->yAxisNo == 0) pa = &yAxis; 00508 else pa = &zAxis; 00509 00510 switch(g->Style) { 00511 case GRAPHSTYLE_SOLID: // ***** solid line **************************** 00512 case GRAPHSTYLE_DASH: 00513 case GRAPHSTYLE_DOT: 00514 case GRAPHSTYLE_LONGDASH: 00515 00516 for(i=g->countY; i>0; i--) { // every branch of curves 00517 px = g->axis(0)->Points; 00518 calcCoordinateP(px, pz, py, p, pa); 00519 ++px; 00520 pz += 2; 00521 ++p; 00522 for(z=g->axis(0)->count-1; z>0; z--) { // every point 00523 FIT_MEMORY_SIZE; // need to enlarge memory block ? 00524 calcCoordinateP(px, pz, py, p, pa); 00525 ++px; 00526 pz += 2; 00527 ++p; 00528 if(Counter >= 2) // clipping only if an axis is manual 00529 clip(p); 00530 } 00531 if((p-3)->isStrokeEnd() && !(p-3)->isBranchEnd()) 00532 p -= 3; // no single point after "no stroke" 00533 else if((p-2)->isBranchEnd() && !(p-1)->isGraphEnd()) { 00534 if((!(p-1)->isPt())) 00535 --p; // erase last hidden point 00536 } 00537 (p++)->setBranchEnd(); 00538 } 00539 00540 p->setGraphEnd(); 00541 /*z = p-g->Points+1; 00542 p = g->Points; 00543 qDebug("\n****** p=%p", p); 00544 for(int zz=0; zz<z; zz+=2) 00545 qDebug("c: %d/%d", *(p+zz), *(p+zz+1));*/ 00546 return; 00547 00548 default: // symbol (e.g. star) at each point ********************** 00549 for(i=g->countY; i>0; i--) { // every branch of curves 00550 px = g->axis(0)->Points; 00551 for(z=g->axis(0)->count; z>0; z--) { // every point 00552 calcCoordinateP(px, pz, py, p, pa); 00553 ++px; 00554 pz += 2; 00555 if(insideDiagramP(p)) // within diagram ? 00556 ++p; 00557 } 00558 (p++)->setBranchEnd(); 00559 assert(p!=g->end()); 00560 } 00561 (p++)->setGraphEnd(); 00562 /*qDebug("\n******"); 00563 for(int zz=0; zz<60; zz+=2) 00564 qDebug("c: %d/%d", *(g->Points+zz), *(g->Points+zz+1));*/ 00565 return; 00566 } 00567 00568 // unreachable 00569 } 00570 00571 // ------------------------------------------------------- 00572 void Diagram::Bounding(int& _x1, int& _y1, int& _x2, int& _y2) 00573 { 00574 _x1 = cx - Bounding_x1; 00575 _y1 = cy - y2 - Bounding_y2; 00576 _x2 = cx + x2 + Bounding_x2; 00577 _y2 = cy - Bounding_y1; 00578 } 00579 00580 // ------------------------------------------------------- 00581 bool Diagram::getSelected(int x_, int y_) 00582 { 00583 if(x_ >= cx-x1) if(x_ <= cx+x3) if(y_ >= cy-y2) if(y_ <= cy+y1) 00584 return true; 00585 00586 return false; 00587 } 00588 00593 bool Diagram::resizeTouched(float fX, float fY, float len) 00594 { 00595 float fCX = float(cx), fCY = float(cy); 00596 float fX2 = float(cx+x2), fY2 = float(cy-y2); 00597 if(fX < fCX-len) return false; 00598 if(fX > fX2+len) return false; 00599 if(fY < fY2-len) return false; 00600 if(fY > fCY+len) return false; 00601 00602 State = 0; 00603 if(fX < fCX+len) State = 1; 00604 else if(fX <= fX2-len) return false; 00605 if(fY > fCY-len) State |= 2; 00606 else if(fY >= fY2+len) return false; 00607 00608 return true; 00609 } 00610 00611 // -------------------------------------------------------------------------- 00612 void Diagram::getAxisLimits(Graph *pg) 00613 { 00614 // FIXME: Graph should know the limits. but it doesn't yet. 00615 // we should only copy here. better: just wrap, dont use {x,y,z}Axis 00616 int z; 00617 double x, y, *p; 00618 DataX const *pD = pg->axis(0); 00619 if(pD == 0) return; 00620 00621 if(Name[0] != 'C') { // not for location curves 00622 p = pD->Points; 00623 for(z=pD->count; z>0; z--) { // check x coordinates (1. dimension) 00624 x = *(p++); 00625 if(std::isfinite(x)) { 00626 if(x > xAxis.max) xAxis.max = x; 00627 if(x < xAxis.min) xAxis.min = x; 00628 } 00629 } 00630 } 00631 00632 if(Name == "Rect3D") { 00633 DataX const *pDy = pg->axis(1); 00634 if(pDy) { 00635 p = pDy->Points; 00636 for(z=pDy->count; z>0; z--) { // check y coordinates (2. dimension) 00637 y = *(p++); 00638 if(std::isfinite(y)) { 00639 if(y > yAxis.max) yAxis.max = y; 00640 if(y < yAxis.min) yAxis.min = y; 00641 } 00642 } 00643 } 00644 } 00645 00646 Axis *pa; 00647 if(pg->yAxisNo == 0) pa = &yAxis; 00648 else pa = &zAxis; 00649 (pa->numGraphs)++; // count graphs 00650 p = pg->cPointsY; 00651 if(p == 0) return; // if no data => invalid 00652 for(z=pg->countY*pD->count; z>0; z--) { // check every y coordinate 00653 x = *(p++); 00654 y = *(p++); 00655 00656 if(Name[0] != 'C') { 00657 if(fabs(y) >= 1e-250) x = sqrt(x*x+y*y); 00658 if(std::isfinite(x)) { 00659 if(x > pa->max) pa->max = x; 00660 if(x < pa->min) pa->min = x; 00661 } 00662 } 00663 else { // location curve needs different treatment 00664 if(std::isfinite(x)) { 00665 if(x > xAxis.max) xAxis.max = x; 00666 if(x < xAxis.min) xAxis.min = x; 00667 } 00668 if(std::isfinite(y)) { 00669 if(y > pa->max) pa->max = y; 00670 if(y < pa->min) pa->min = y; 00671 } 00672 } 00673 } 00674 } 00675 00676 // -------------------------------------------------------------------------- 00677 void Diagram::loadGraphData(const QString& defaultDataSet) 00678 { 00679 int yNum = yAxis.numGraphs; 00680 int zNum = zAxis.numGraphs; 00681 yAxis.numGraphs = zAxis.numGraphs = 0; 00682 00683 double xmin = xAxis.min, ymin = yAxis.min, zmin = zAxis.min; 00684 double xmax = xAxis.max, ymax = yAxis.max, zmax = zAxis.max; 00685 yAxis.min = zAxis.min = xAxis.min = DBL_MAX; 00686 yAxis.max = zAxis.max = xAxis.max = -DBL_MAX; 00687 00688 int No=0; 00689 foreach(Graph *pg, Graphs) { 00690 qDebug() << "load GraphData load" << defaultDataSet << pg->Var; 00691 if(pg->loadDatFile(defaultDataSet) != 1) // load data, determine max/min values 00692 No++; 00693 getAxisLimits(pg); 00694 } 00695 00696 if(No <= 0) { // All dataset files unchanged ? 00697 yAxis.numGraphs = yNum; // rebuild scrollbar position 00698 zAxis.numGraphs = zNum; 00699 00700 xAxis.min = xmin; yAxis.min = ymin; zAxis.min = zmin; 00701 xAxis.max = xmax; yAxis.max = ymax; zAxis.max = zmax; 00702 return; // -> no update neccessary 00703 } 00704 00705 if(xAxis.min > xAxis.max) 00706 xAxis.min = xAxis.max = 0.0; 00707 if(yAxis.min > yAxis.max) 00708 yAxis.min = yAxis.max = 0.0; 00709 if(zAxis.min > zAxis.max) 00710 zAxis.min = zAxis.max = 0.0; 00711 00712 /* if((Name == "Polar") || (Name == "Smith")) { // one axis only 00713 if(yAxis.min > zAxis.min) yAxis.min = zAxis.min; 00714 if(yAxis.max < zAxis.max) yAxis.max = zAxis.max; 00715 }*/ 00716 updateGraphData(); 00717 } 00718 00722 void Diagram::recalcGraphData() 00723 { 00724 yAxis.min = zAxis.min = xAxis.min = DBL_MAX; 00725 yAxis.max = zAxis.max = xAxis.max = -DBL_MAX; 00726 yAxis.numGraphs = zAxis.numGraphs = 0; 00727 00728 // get maximum and minimum values 00729 foreach(Graph *pg, Graphs) 00730 getAxisLimits(pg); 00731 00732 if(xAxis.min > xAxis.max) { 00733 xAxis.min = 0.0; 00734 xAxis.max = 1.0; 00735 } 00736 if(yAxis.min > yAxis.max) { 00737 yAxis.min = 0.0; 00738 yAxis.max = 1.0; 00739 } 00740 if(zAxis.min > zAxis.max) { 00741 zAxis.min = 0.0; 00742 zAxis.max = 1.0; 00743 } 00744 if((Name == "Polar") || (Name == "Smith")) { // one axis only 00745 if(yAxis.min > zAxis.min) yAxis.min = zAxis.min; 00746 if(yAxis.max < zAxis.max) yAxis.max = zAxis.max; 00747 } 00748 00749 updateGraphData(); 00750 } 00751 00752 // ------------------------------------------------------------------------ 00753 void Diagram::updateGraphData() 00754 { 00755 int valid = calcDiagram(); // do not calculate graph data if invalid 00756 00757 foreach(Graph *pg, Graphs) { 00758 pg->clear(); 00759 if((valid & (pg->yAxisNo+1)) != 0) 00760 calcData(pg); // calculate screen coordinates 00761 else if(pg->cPointsY) { 00762 delete[] pg->cPointsY; 00763 pg->cPointsY = 0; 00764 } 00765 } 00766 00767 createAxisLabels(); // virtual function 00768 00769 // Setting markers must be done last, because in 3D diagram "Mem" 00770 // is released in "createAxisLabels()". 00771 foreach(Graph *pg, Graphs){ 00772 pg->createMarkerText(); 00773 } 00774 } 00775 00776 // -------------------------------------------------------------------------- 00783 int Graph::loadDatFile(const QString& fileName) 00784 { 00785 Graph* g = this; 00786 QFile file; 00787 QString Variable; 00788 QFileInfo Info(fileName); 00789 00790 int pos = g->Var.indexOf(':'); 00791 // if(g->Var.right(3) == "].X") // e.g. stdl[8:0].X 00792 // if(pos > g->Var.indexOf('[')) 00793 // pos = -1; 00794 00795 /* WORK-AROUND: A bug in SCIM (libscim) which Qt is linked to causes 00796 to change the locale to the default. */ 00797 setlocale (LC_NUMERIC, "C"); 00798 00799 if(pos <= 0) { 00800 file.setFileName(fileName); 00801 Variable = g->Var; 00802 } 00803 else { 00804 file.setFileName(Info.path()+QDir::separator() + g->Var.left(pos)+".dat"); 00805 Variable = g->Var.mid(pos+1); 00806 } 00807 00808 Info.setFile(file); 00809 if(g->lastLoaded.isValid()) 00810 if(g->lastLoaded > Info.lastModified()) 00811 return 1; // dataset unchanged -> no update neccessary 00812 00813 g->countY = 0; 00814 g->mutable_axes().clear(); // HACK 00815 if(g->cPointsY) { delete[] g->cPointsY; g->cPointsY = 0; } 00816 if(Variable.isEmpty()) return 0; 00817 00818 #if 0 // FIXME encapsulation. implement digital waves later. 00819 if(Variable.right(2) == ".X") 00820 if(Name.at(0) != 'T') 00821 return 0; // digital variables only for tabulars and ziming diagram 00822 #endif 00823 00824 00825 if(!file.open(QIODevice::ReadOnly)) return 0; 00826 00827 // ***************************************************************** 00828 // To strongly speed up the file read operation the whole file is 00829 // read into the memory in one piece. 00830 QByteArray FileContent; 00831 FileContent = file.readAll(); 00832 file.close(); 00833 char *FileString = FileContent.data(); 00834 if(!FileString) return 0; 00835 char *pPos = FileString+FileContent.size()-1; 00836 if(*pPos > ' ') if(*pPos != '>') return 0; 00837 *pPos = 0; 00838 00839 00840 // ***************************************************************** 00841 // look for variable name in data file **************************** 00842 bool isIndep = false; 00843 Variable = "dep "+Variable+" "; 00844 // "pFile" is used through-out the whole function and must NOT used 00845 // for other purposes! 00846 char *pFile = strstr(FileString, Variable.toLatin1()); 00847 while(pFile) { 00848 if(*(pFile-1) == '<') // is dependent variable ? 00849 break; 00850 else if(strncmp(pFile-3, "<in", 3) == 0) { // is independent variable ? 00851 isIndep = true; 00852 break; 00853 } 00854 pFile = strstr(pFile+4, Variable.toLatin1()); 00855 } 00856 00857 if(!pFile) return 0; // data not found 00858 00859 QString Line, tmp; 00860 pFile += Variable.length(); 00861 pPos = strchr(pFile, '>'); 00862 if(!pPos) return 0; // file corrupt 00863 *pPos = 0; 00864 Line = QString(pFile); 00865 *pPos = '>'; 00866 pFile = pPos+1; 00867 if(!isIndep) { 00868 pos = 0; 00869 tmp = Line.section(' ', pos, pos); 00870 while(!tmp.isEmpty()) { 00871 g->mutable_axes().push_back(new DataX(tmp)); // name of independet variable 00872 pos++; 00873 tmp = Line.section(' ', pos, pos); 00874 } 00875 } 00876 00877 // ***************************************************************** 00878 // get independent variable **************************************** 00879 bool ok=true; 00880 double *p; 00881 int counting = 0; 00882 if(isIndep) { // create independent variable by myself ? 00883 counting = Line.toInt(&ok); // get number of values 00884 g->mutable_axes().push_back(new DataX("number", 0, counting)); 00885 if(!ok) return 0; 00886 00887 p = new double[counting]; // memory of new independent variable 00888 g->countY = 1; 00889 g->mutable_axes().back()->Points = p; 00890 for(int z=1; z<=counting; z++) *(p++) = double(z); 00891 auto Axis = g->mutable_axes().back(); 00892 Axis->min(1.); 00893 Axis->max(double(counting)); 00894 } 00895 else { // ................................... 00896 // get independent variables from data file 00897 g->countY = 1; 00898 #if 0 // FIXME: we do not have a Name. 00899 DataX *bLast = 0; 00900 if(Name == "Rect3D") bLast = g->axis(1); // y axis for Rect3D 00901 #endif 00902 00903 #if 0 // FIXME: this is about diagram. do after load. 00904 double min_tmp = xAxis.min, max_tmp = xAxis.max; 00905 #endif 00906 DataX const *pD; 00907 for(int ii= g->numAxes(); (pD = g->axis(--ii)); ) { 00908 #if 0 // FIXME: this is about diagram. do after load. 00909 pa = &xAxis; 00910 if(pD == g->axis(0)) { 00911 xAxis.min = min_tmp; // only count first independent variable 00912 xAxis.max = max_tmp; 00913 } 00914 else if(pD == bLast) pa = &yAxis; // y axis for Rect3D 00915 #endif 00916 counting = loadIndepVarData(pD->Var, FileString, mutable_axis(ii)); 00917 if(counting <= 0) return 0; 00918 00919 g->countY *= counting; 00920 } 00921 g->countY /= counting; 00922 } 00923 00924 00925 // ***************************************************************** 00926 // get dependent variables ***************************************** 00927 counting *= g->countY; 00928 p = new double[2*counting]; // memory for dependent variables 00929 g->cPointsY = p; 00930 #if 0 // FIXME: what does this do?! 00931 if(g->yAxisNo == 0) pa = &yAxis; // for which axis 00932 else pa = &zAxis; 00933 (pa->numGraphs)++; // count graphs 00934 #endif 00935 00936 char *pEnd; 00937 double x, y; 00938 pPos = pFile; 00939 00940 if(Variable.right(3) != ".X ") { // not "digital" 00941 00942 for(int z=counting; z>0; z--) { 00943 pEnd = 0; 00944 while((*pPos) && (*pPos <= ' ')) pPos++; // find start of next number 00945 x = strtod(pPos, &pEnd); // real part 00946 pPos = pEnd + 1; 00947 if(*pEnd < ' ') // is there an imaginary part ? 00948 y = 0.0; 00949 else { 00950 if(((*pEnd != '+') && (*pEnd != '-')) || (*pPos != 'j')) { 00951 delete[] g->cPointsY; g->cPointsY = 0; 00952 return 0; 00953 } 00954 *pPos = *pEnd; // overwrite 'j' with sign 00955 pEnd = 0; 00956 y = strtod(pPos, &pEnd); // imaginary part 00957 *pPos = 'j'; // write back old character 00958 pPos = pEnd; 00959 } 00960 *(p++) = x; 00961 *(p++) = y; 00962 #if 0 // FIXME there is no Name here. 00963 if(Name[0] != 'C') 00964 #endif 00965 { 00966 if(fabs(y) >= 1e-250) x = sqrt(x*x+y*y); 00967 if(std::isfinite(x)) { 00968 auto Axis = g->mutable_axes().back(); 00969 Axis->min(x); 00970 Axis->max(x); 00971 } 00972 } 00973 00974 #if 0 // this is not location curce code. 00975 else { // location curve needs different treatment 00976 if(std::isfinite(x)) { 00977 if(x > xAxis.max) xAxis.max = x; 00978 if(x < xAxis.min) xAxis.min = x; 00979 } 00980 if(std::isfinite(y)) { 00981 if(y > pa->max) pa->max = y; 00982 if(y < pa->min) pa->min = y; 00983 } 00984 } 00985 #endif 00986 } 00987 00988 } else { // of "if not digital" 00989 00990 char *pc = (char*)p; 00991 pEnd = pc + 2*(counting-1)*sizeof(double); 00992 // for digital variables (e.g. 100ZX0): 00993 for(int z=counting; z>0; z--) { 00994 00995 while((*pPos) && (*pPos <= ' ')) pPos++; // find start of next bit vector 00996 if(*pPos == 0) { 00997 delete[] g->cPointsY; g->cPointsY = 0; 00998 return 0; 00999 } 01000 01001 while(*pPos > ' ') { // copy bit vector 01002 *(pc++) = *(pPos++); 01003 if(pEnd <= pc) { 01004 counting = pc - (char*)g->cPointsY; 01005 pc = (char*)realloc(g->cPointsY, counting+1024); 01006 pEnd = pc; 01007 g->cPointsY = (double*)pEnd; 01008 pc += counting; 01009 pEnd += counting+1020; 01010 } 01011 } 01012 *(pc++) = 0; // terminate each vector with NULL 01013 } 01014 01015 } // of "if not digital" 01016 01017 lastLoaded = QDateTime::currentDateTime(); 01018 return 2; 01019 } 01020 01024 int Graph::loadIndepVarData(const QString& Variable, 01025 char *FileString, DataX* pD) 01026 { 01027 bool isIndep = false; 01028 QString Line, tmp; 01029 01030 /* WORK-AROUND: A bug in SCIM (libscim) which Qt is linked to causes 01031 to change the locale to the default. */ 01032 setlocale (LC_NUMERIC, "C"); 01033 01034 Line = "dep "+Variable+" "; 01035 // "pFile" is used through-out the whole function and must NOT used 01036 // for other purposes! 01037 char *pFile = strstr(FileString, Line.toLatin1()); 01038 while(pFile) { 01039 if(*(pFile-1) == '<') // is dependent variable ? 01040 break; 01041 else if(strncmp(pFile-3, "<in", 3) == 0) { // is independent variable ? 01042 isIndep = true; 01043 break; 01044 } 01045 pFile = strstr(pFile+4, Line.toLatin1()); 01046 } 01047 01048 if(!pFile) return -1; // data not found 01049 01050 pFile += Line.length(); 01051 char *pPos = strchr(pFile, '>'); 01052 if(!pPos) return -1; // file corrupt 01053 *pPos = 0; 01054 Line = QString(pFile); 01055 *pPos = '>'; 01056 pFile = pPos+1; 01057 char *pEnd; 01058 if(!isIndep) { // dependent variable can also be used... 01059 if(Line.indexOf(' ') >= 0) return -1; // ...if only one dependency 01060 Line = "<indep "+Line+" "; 01061 pPos = strstr(FileString, Line.toLatin1()); 01062 if(!pPos) return -1; 01063 pPos += Line.length(); 01064 pEnd = strchr(pPos, '>'); 01065 if(!pEnd) return -1; // file corrupt 01066 *pEnd = 0; 01067 Line = QString(pPos); 01068 *pEnd = '>'; 01069 } 01070 01071 01072 bool ok; 01073 int n = Line.toInt(&ok); // number of values 01074 if(!ok) return -1; 01075 01076 double *p = new double[n]; // memory for new independent variable 01077 // DataX *pD = pg->mutable_axes().back(); 01078 pD->Points = p; 01079 pD->count = n; 01080 01081 01082 double x; 01083 pPos = pFile; 01084 // find first position containing no whitespace 01085 while((*pPos) && (*pPos <= ' ')) pPos++; 01086 01087 for(int z=0; z<n; z++) { 01088 pEnd = 0; 01089 x = strtod(pPos, &pEnd); // real part 01090 if(pPos == pEnd) { 01091 delete[] pD->Points; pD->Points = 0; 01092 return -1; 01093 } 01094 01095 *(p++) = x; 01096 #if 0 // this is not location curve code 01097 if(Name[0] != 'C') // not for location curves 01098 if(std::isfinite(x)) { 01099 if(x > pa->max) pa->max = x; 01100 if(x < pa->min) pa->min = x; 01101 } 01102 #endif 01103 01104 pPos = pEnd; 01105 while((*pPos) && (*pPos <= ' ')) pPos++; // find start of next number 01106 } 01107 01108 return n; // return number of independent data 01109 } 01110 01114 bool Diagram::sameDependencies(Graph const*g1, Graph const*g2) const 01115 { 01116 // FIXME 01117 // return g1->same(*g2); 01118 if(g1 == g2) return true; 01119 if(g1->numAxes()!=g2->numAxes()) return false; 01120 01121 for(unsigned i=0; i<g1->numAxes(); ++i) { 01122 if(g1->axisName(i) != g2->axisName(i)) return false; 01123 } 01124 01125 return true; 01126 } 01127 01128 // ------------------------------------------------------------ 01129 int Diagram::checkColumnWidth(const QString& Str, 01130 const QFontMetrics& metrics, int colWidth, int x, int y) 01131 { 01132 //qDebug("%i", metrics.charWidth(Str,0)); 01133 int w = metrics.boundingRect(Str).width(); // width of text 01134 if(w > colWidth) { 01135 colWidth = w; 01136 if((x+colWidth) >= x2) { // enough space for text ? 01137 // mark lack of space with a small arrow 01138 Lines.append(new Line(x2-6, y-4, x2+7, y-4, QPen(Qt::red,2))); 01139 Lines.append(new Line(x2, y-7, x2+6, y-4, QPen(Qt::red,2))); 01140 Lines.append(new Line(x2, y-1, x2+6, y-4, QPen(Qt::red,2))); 01141 return -1; 01142 } 01143 } 01144 return colWidth; 01145 } 01146 01147 // ------------------------------------------------------------ 01148 void Diagram::setCenter(int x, int y, bool relative) 01149 { 01150 if(relative) { 01151 cx += x; cy += y; 01152 } 01153 else { 01154 cx = x; cy = y; 01155 } 01156 } 01157 01158 // ------------------------------------------------------- 01159 void Diagram::getCenter(int& x, int& y) 01160 { 01161 x = cx + (x2 >> 1); 01162 y = cy - (y2 >> 1); 01163 } 01164 01165 // ------------------------------------------------------------ 01166 Diagram* Diagram::newOne() 01167 { 01168 return new Diagram(); 01169 } 01170 01171 // ------------------------------------------------------------ 01172 void Diagram::finishMarkerCoordinates(float& fCX, float& fCY) const 01173 { 01174 if(!insideDiagram(fCX, fCY)) { 01175 fCX = float(x2 >> 1); 01176 fCY = float(y2 >> 1); 01177 } 01178 } 01179 01180 // ------------------------------------------------------------ 01181 QString Diagram::save() 01182 { 01183 QString s = "<"+Name+" "+QString::number(cx)+" "+QString::number(cy)+" "; 01184 s += QString::number(x2)+" "+QString::number(y2)+" "; 01185 char c = '0'; 01186 if(xAxis.GridOn) c |= 1; 01187 if(hideLines) c |= 2; 01188 s += c; 01189 s += " " + GridPen.color().name() + " " + QString::number(GridPen.style()); 01190 01191 if(xAxis.log) s+= " 1"; else s += " 0"; 01192 c = '0'; 01193 if(yAxis.log) c |= 1; 01194 if(zAxis.log) c |= 2; 01195 s += c; 01196 01197 if(xAxis.autoScale) s+= " 1 "; 01198 else s+= " 0 "; 01199 s += QString::number(xAxis.limit_min) + " "; 01200 s += QString::number(xAxis.step) + " "; 01201 s += QString::number(xAxis.limit_max); 01202 if(yAxis.autoScale) s+= " 1 "; 01203 else s+= " 0 "; 01204 s += QString::number(yAxis.limit_min) + " "; 01205 s += QString::number(yAxis.step) + " "; 01206 s += QString::number(yAxis.limit_max); 01207 if(zAxis.autoScale) s+= " 1 "; 01208 else s+= " 0 "; 01209 s += QString::number(zAxis.limit_min) + " "; 01210 s += QString::number(zAxis.step) + " "; 01211 s += QString::number(zAxis.limit_max) + " "; 01212 01213 s += QString::number(rotX)+" "+QString::number(rotY)+" "+ 01214 QString::number(rotZ); 01215 01216 // labels can contain spaces -> must be last items in the line 01217 s += " \""+xAxis.Label+"\" \""+yAxis.Label+"\" \""+zAxis.Label+"\">\n"; 01218 01219 foreach(Graph *pg, Graphs) 01220 s += pg->save()+"\n"; 01221 01222 s += " </"+Name+">"; 01223 return s; 01224 } 01225 01226 // ------------------------------------------------------------ 01227 bool Diagram::load(const QString& Line, QTextStream *stream) 01228 { 01229 bool ok; 01230 QString s = Line; 01231 01232 if(s.at(0) != '<') return false; 01233 if(s.at(s.length()-1) != '>') return false; 01234 s = s.mid(1, s.length()-2); // cut off start and end character 01235 01236 QString n; 01237 n = s.section(' ',1,1); // cx 01238 cx = n.toInt(&ok); 01239 if(!ok) return false; 01240 01241 n = s.section(' ',2,2); // cy 01242 cy = n.toInt(&ok); 01243 if(!ok) return false; 01244 01245 n = s.section(' ',3,3); // x2 01246 x2 = n.toInt(&ok); 01247 if(!ok) return false; 01248 01249 n = s.section(' ',4,4); // y2 01250 y2 = n.toInt(&ok); 01251 if(!ok) return false; 01252 01253 char c; 01254 n = s.section(' ',5,5); // GridOn 01255 c = n.at(0).toLatin1() - '0'; 01256 xAxis.GridOn = yAxis.GridOn = (c & 1) != 0; 01257 hideLines = (c & 2) != 0; 01258 01259 n = s.section(' ',6,6); // color for GridPen 01260 QColor co; 01261 co.setNamedColor(n); 01262 GridPen.setColor(co); 01263 if(!GridPen.color().isValid()) return false; 01264 01265 n = s.section(' ',7,7); // line style 01266 GridPen.setStyle((Qt::PenStyle)n.toInt(&ok)); 01267 if(!ok) return false; 01268 01269 n = s.section(' ',8,8); // xlog, ylog 01270 xAxis.log = n.at(0) != '0'; 01271 c = n.at(1).toLatin1(); 01272 yAxis.log = ((c - '0') & 1) == 1; 01273 zAxis.log = ((c - '0') & 2) == 2; 01274 01275 n = s.section(' ',9,9); // xAxis.autoScale 01276 if(n.at(0) != '"') { // backward compatible 01277 if(n == "1") xAxis.autoScale = true; 01278 else xAxis.autoScale = false; 01279 01280 n = s.section(' ',10,10); // xAxis.limit_min 01281 xAxis.limit_min = n.toDouble(&ok); 01282 if(!ok) return false; 01283 01284 n = s.section(' ',11,11); // xAxis.step 01285 xAxis.step = n.toDouble(&ok); 01286 if(!ok) return false; 01287 01288 n = s.section(' ',12,12); // xAxis.limit_max 01289 xAxis.limit_max = n.toDouble(&ok); 01290 if(!ok) return false; 01291 01292 n = s.section(' ',13,13); // yAxis.autoScale 01293 if(n == "1") yAxis.autoScale = true; 01294 else yAxis.autoScale = false; 01295 01296 n = s.section(' ',14,14); // yAxis.limit_min 01297 yAxis.limit_min = n.toDouble(&ok); 01298 if(!ok) return false; 01299 01300 n = s.section(' ',15,15); // yAxis.step 01301 yAxis.step = n.toDouble(&ok); 01302 if(!ok) return false; 01303 01304 n = s.section(' ',16,16); // yAxis.limit_max 01305 yAxis.limit_max = n.toDouble(&ok); 01306 if(!ok) return false; 01307 01308 n = s.section(' ',17,17); // zAxis.autoScale 01309 if(n == "1") zAxis.autoScale = true; 01310 else zAxis.autoScale = false; 01311 01312 n = s.section(' ',18,18); // zAxis.limit_min 01313 zAxis.limit_min = n.toDouble(&ok); 01314 if(!ok) return false; 01315 01316 n = s.section(' ',19,19); // zAxis.step 01317 zAxis.step = n.toDouble(&ok); 01318 if(!ok) return false; 01319 01320 n = s.section(' ',20,20); // zAxis.limit_max 01321 zAxis.limit_max = n.toDouble(&ok); 01322 if(!ok) return false; 01323 01324 n = s.section(' ',21,21); // rotX 01325 if(n.at(0) != '"') { // backward compatible 01326 rotX = n.toInt(&ok); 01327 if(!ok) return false; 01328 01329 n = s.section(' ',22,22); // rotY 01330 rotY = n.toInt(&ok); 01331 if(!ok) return false; 01332 01333 n = s.section(' ',23,23); // rotZ 01334 rotZ = n.toInt(&ok); 01335 if(!ok) return false; 01336 } 01337 } 01338 01339 xAxis.Label = s.section('"',1,1); // xLabel 01340 yAxis.Label = s.section('"',3,3); // yLabel left 01341 zAxis.Label = s.section('"',5,5); // yLabel right 01342 01343 Graph *pg; 01344 // ....................................................... 01345 // load graphs of the diagram 01346 while(!stream->atEnd()) { 01347 s = stream->readLine(); 01348 s = s.trimmed(); 01349 if(s.isEmpty()) continue; 01350 01351 if(s == ("</"+Name+">")) return true; // found end tag ? 01352 if(s.section(' ', 0,0) == "<Mkr") { 01353 01354 // ....................................................... 01355 // load markers of the diagram 01356 pg = Graphs.last(); 01357 if(!pg) return false; 01358 assert(pg->parentDiagram() == this); 01359 Marker *pm = new Marker(pg); 01360 if(!pm->load(s)) { 01361 delete pm; 01362 return false; 01363 } 01364 pg->Markers.append(pm); 01365 continue; 01366 } 01367 01368 pg = new Graph(this); 01369 if(!pg->load(s)) { 01370 delete pg; 01371 return false; 01372 } 01373 Graphs.append(pg); 01374 } 01375 01376 return false; // end tag missing 01377 } 01378 01379 // -------------------------------------------------------------- 01380 void Diagram::calcSmithAxisScale(Axis *Axis, int& GridX, int& GridY) 01381 { 01382 xAxis.low = xAxis.min; 01383 xAxis.up = xAxis.max; 01384 01385 Axis->low = 0.0; 01386 if(fabs(Axis->min) > Axis->max) 01387 Axis->max = fabs(Axis->min); // also fit negative values 01388 if(Axis->autoScale) { 01389 if(Axis->max > 1.01) Axis->up = 1.05*Axis->max; 01390 else Axis->up = 1.0; 01391 GridX = GridY = 4; 01392 } 01393 else { 01394 Axis->up = Axis->limit_max = fabs(Axis->limit_max); 01395 GridX = GridY = int(Axis->step); 01396 } 01397 } 01398 01399 // ------------------------------------------------------------ 01400 void Diagram::createSmithChart(Axis *Axis, int Mode) 01401 { 01402 int GridX; // number of arcs with re(z)=const 01403 int GridY; // number of arcs with im(z)=const 01404 calcSmithAxisScale(Axis, GridX, GridY); 01405 01406 01407 if(!xAxis.GridOn) return; 01408 01409 bool Zplane = ((Mode & 1) == 1); // impedance or admittance chart ? 01410 bool Above = ((Mode & 2) == 2); // paint upper half ? 01411 bool Below = ((Mode & 4) == 4); // paint lower half ? 01412 01413 int dx2 = x2>>1; 01414 01415 double im, n_cos, n_sin, real, real1, real2, root; 01416 double rMAXq = Axis->up*Axis->up; 01417 int theta, beta, phi, len, m, x, y; 01418 01419 int R1 = int(x2/Axis->up + 0.5); 01420 // .................................................... 01421 // draw arcs with im(z)=const 01422 for(m=1; m<GridY; m++) { 01423 n_sin = pi*double(m)/double(GridY); 01424 n_cos = cos(n_sin); 01425 n_sin = sin(n_sin); 01426 im = (1.0-n_cos)/n_sin * pow(Axis->up,0.7); // up^0.7 is beauty correction 01427 y = int(im/Axis->up*x2 + 0.5); // diameter 01428 01429 if(Axis->up <= 1.0) { // Smith chart with |r|=1 01430 beta = int(16.0*180.0*atan2(n_sin-im,n_cos-1.0)/pi - 0.5); 01431 if(beta<0) beta += 16*360; 01432 theta = 16*270-beta; 01433 } 01434 else { // Smith chart with |r|>1 01435 im = 1.0/im; 01436 real = (rMAXq+1.0)/(rMAXq-1.0); 01437 root = real*real - im*im - 1.0; 01438 if(root < 0.0) { // circle lies completely within the Smith chart ? 01439 beta = 0; // yes, ... 01440 theta = 16*360; // ... draw whole circle 01441 } 01442 else { 01443 // calculate both intersections with most outer circle 01444 real1 = sqrt(root)-real; 01445 real2 = -sqrt(root)-real; 01446 01447 root = (real1+1.0)*(real1+1.0) + im*im; 01448 n_cos = (real1*real1 + im*im - 1.0) / root; 01449 n_sin = 2.0*im / root; 01450 beta = int(16.0*180.0*atan2(n_sin-1.0/im,n_cos-1.0)/pi); 01451 if(beta<0) beta += 16*360; 01452 01453 root = (real2+1.0)*(real2+1.0) + im*im; 01454 n_cos = (real2*real2 + im*im - 1.0) / root; 01455 n_sin = 2.0*im / root; 01456 theta = int(16.0*180.0*atan2(n_sin-1/im,n_cos-1)/pi); 01457 if(theta<0) theta += 16*360; 01458 theta = theta - beta; // arc length 01459 if(theta < 0) theta = 16*360+theta; 01460 } 01461 } 01462 01463 if(Zplane) 01464 x = (x2 + R1 - y) >> 1; 01465 else { 01466 x = (x2 - R1 - y) >> 1; 01467 beta = 16*180 - beta - theta; // mirror 01468 if(beta < 0) beta += 16*360; // angle has to be > 0 01469 } 01470 01471 if(Above) 01472 Arcs.append(new struct Arc(x, dx2+y, y, y, beta, theta, GridPen)); 01473 if(Below) 01474 Arcs.append(new struct Arc(x, dx2, y, y, 16*360-beta-theta, theta, GridPen)); 01475 } 01476 01477 // .................................................... 01478 // draw arcs with Re(z)=const 01479 theta = 0; // arc length 01480 beta = 16*180; // start angle 01481 if(Above) { beta = 0; theta = 16*180; } 01482 if(Below) theta += 16*180; 01483 01484 for(m=1; m<GridX; m++) { 01485 im = m*(Axis->up+1.0)/GridX - Axis->up; 01486 y = int((1.0-im)/Axis->up*double(dx2) + 0.5); // diameter 01487 01488 if(Zplane) 01489 x = ((x2+R1)>>1) - y; 01490 else 01491 x = (x2-R1)>>1; 01492 if(fabs(fabs(im)-1.0) > 0.2) // if too near to |r|=1, it looks ugly 01493 Arcs.append(new struct Arc(x, (x2+y)>>1, y, y, beta, theta, GridPen)); 01494 01495 if(Axis->up > 1.0) { // draw arcs on the rigth-handed side ? 01496 im = 1.0-im; 01497 im = (rMAXq-1.0)/(im*(im/2.0+1.0)) - 1.0; 01498 if(Zplane) x += y; 01499 else x -= y; 01500 if(im >= 1.0) 01501 Arcs.append(new struct Arc(x, (x2+y)>>1, y, y, beta, theta, GridPen)); 01502 else { 01503 phi = int(16.0*180.0/pi*acos(im)); 01504 len = 16*180-phi; 01505 if(Above && Below) len += len; 01506 else if(Below) phi = 16*180; 01507 if(!Zplane) phi += 16*180; 01508 Arcs.append(new struct Arc(x, (x2+y)>>1, y, y, phi, len, GridPen)); 01509 } 01510 } 01511 } 01512 01513 01514 // .................................................... 01515 if(Axis->up > 1.0) { // draw circle with |r|=1 ? 01516 x = (x2-R1) >> 1; 01517 y = (x2+R1) >> 1; 01518 Arcs.append(new struct Arc(x, y, R1, R1, beta, theta, QPen(Qt::black,0))); 01519 01520 // vertical line Re(r)=1 (visible only if |r|>1) 01521 if(Zplane) x = y; 01522 y = int(sqrt(rMAXq-1)/Axis->up*dx2 + 0.5); 01523 if(Above) m = y; 01524 else m = 0; 01525 if(!Below) y = 0; 01526 Lines.append(new Line(x, dx2+m, x, dx2-y, GridPen)); 01527 01528 if(Below) y = 4; 01529 else y = y2-4-QucsSettings.font.pointSize(); 01530 Texts.append(new Text(0, y, misc::StringNum(Axis->up))); 01531 } 01532 01533 } 01534 01535 01536 // -------------------------------------------------------------- 01537 void Diagram::calcPolarAxisScale(Axis *Axis, double& numGrids, 01538 double& GridStep, double& zD) 01539 { 01540 if(Axis->autoScale) { // auto-scale or user defined limits ? 01541 double Expo, Base; 01542 numGrids = floor(double(x2)/80.0); // minimal grid is 40 pixel 01543 Expo = floor(log10(Axis->max/numGrids)); 01544 Base = Axis->max/numGrids/pow(10.0,Expo);// get first significant digit 01545 if(Base < 3.5) { // use only 1, 2 and 5, which ever is best fitted 01546 if(Base < 1.5) Base = 1.0; 01547 else Base = 2.0; 01548 } 01549 else { 01550 if(Base < 7.5) Base = 5.0; 01551 else { Base = 1.0; Expo++; } 01552 } 01553 GridStep = Base * pow(10.0,Expo); // grid distance in real values 01554 numGrids -= floor(numGrids - Axis->max/GridStep); // correct num errors 01555 Axis->up = GridStep*numGrids; 01556 01557 zD = double(x2) / numGrids; // grid distance in pixel 01558 } 01559 else { // no auto-scale 01560 Axis->up = Axis->limit_max = fabs(Axis->limit_max); 01561 GridStep = Axis->step; 01562 zD = double(x2) / Axis->limit_max * Axis->step; // grid distance in pixel 01563 01564 if(fabs(zD) < 2.0) { // if grid too small, then no grid 01565 zD = double(x2); 01566 GridStep = Axis->step = Axis->up; 01567 numGrids = 1.0; 01568 } 01569 else numGrids = Axis->limit_max / Axis->step; 01570 } 01571 } 01572 01573 // ------------------------------------------------------------ 01574 void Diagram::createPolarDiagram(Axis *Axis, int Mode) 01575 { 01576 xAxis.low = xAxis.min; 01577 xAxis.up = xAxis.max; 01578 Axis->low = 0.0; 01579 if(fabs(Axis->min) > Axis->max) 01580 Axis->max = fabs(Axis->min); // also fit negative values 01581 01582 01583 bool Above = ((Mode & 1) == 1); // paint upper half ? 01584 bool Below = ((Mode & 2) == 2); // paint lower half ? 01585 01586 int i, z, tmp; 01587 if(Above) i = y2; else i = y2>>1; 01588 if(Below) z = 0; else z = y2>>1; 01589 // y line 01590 Lines.append(new Line(x2>>1, i, x2>>1, z, GridPen)); 01591 01592 int len = 0; // arc length 01593 int beta = 16*180; // start angle 01594 if(Above) { beta = 0; len = 16*180; } 01595 if(Below) len += 16*180; 01596 01597 int phi, tPos; 01598 int tHeight = QucsSettings.font.pointSize() + 5; 01599 if(!Below) tPos = (y2>>1) + 3; 01600 else tPos = (y2>>1) - tHeight + 3; 01601 01602 double Expo, Base, numGrids, GridStep, zD; 01603 if(xAxis.GridOn) { 01604 calcPolarAxisScale(Axis, numGrids, GridStep, zD); 01605 01606 double zDstep = zD; 01607 double GridNum = 0.0; 01608 for(i=int(numGrids); i>1; i--) { // create all grid circles 01609 z = int(zD); 01610 GridNum += GridStep; 01611 Texts.append(new Text(((x2+z)>>1)-10, tPos, misc::StringNiceNum(GridNum))); 01612 01613 phi = int(16.0*180.0/pi*atan(double(2*tHeight)/zD)); 01614 if(!Below) tmp = beta + phi; 01615 else tmp = beta; 01616 Arcs.append(new struct Arc((x2-z)>>1, (y2+z)>>1, z, z, tmp, len-phi, 01617 GridPen)); 01618 zD += zDstep; 01619 } 01620 } 01621 else { // of "if(GridOn)" 01622 Expo = floor(log10(Axis->max)); 01623 Base = ceil(Axis->max/pow(10.0,Expo) - 0.01); 01624 Axis->up = Base * pow(10.0,Expo); // separate Base * 10^Expo 01625 } 01626 01627 // create outer circle 01628 Texts.append(new Text(x2-8, tPos, misc::StringNiceNum(Axis->up))); 01629 phi = int(16.0*180.0/pi*atan(double(2*tHeight)/double(x2))); 01630 if(!Below) tmp = phi; 01631 else tmp = 0; 01632 Arcs.append(new struct Arc(0, y2, x2, y2, tmp, 16*360-phi, QPen(Qt::black,0))); 01633 01634 // get size of text using the screen-compatible metric 01635 QFontMetrics metrics(QucsSettings.font, 0); 01636 QSize r = metrics.size(0, Texts.last()->s); // width of text 01637 len = x2+r.width()-4; // more space at the right 01638 if(len > x3) x3 = len; 01639 } 01640 01652 bool Diagram::calcAxisScale(Axis *Axis, double& GridNum, double& zD, 01653 double& zDstep, double& GridStep, double Dist) 01654 { 01655 bool back=false; 01656 double numGrids, Base, Expo, corr; 01657 if(Axis->autoScale) { 01658 01659 if(fabs(Axis->max-Axis->min) < 1e-200) { 01660 if((Axis->max == 0.0) && (Axis->min == 0.0)) { 01661 Axis->up = 1.0; 01662 Axis->low = -1.0; 01663 } 01664 else { // if max = min, double difference 01665 Axis->up = Axis->max + fabs(Axis->max); 01666 Axis->low = Axis->min - fabs(Axis->min); 01667 } 01668 } 01669 else if(Axis != &xAxis) { 01670 // keep a small bounding between graph and diagram limit 01671 Axis->up = Axis->max + 0.1*(Axis->max-Axis->min); 01672 Axis->low = Axis->min - 0.1*(Axis->max-Axis->min); 01673 } 01674 else { 01675 Axis->up = Axis->max; // normal case for x axis 01676 Axis->low = Axis->min; 01677 } 01678 01679 01680 numGrids = floor(Dist/60.0); // minimal grid is 60 pixel 01681 if(numGrids < 1.0) Base = Axis->up-Axis->low; 01682 else Base = (Axis->up-Axis->low)/numGrids; 01683 Expo = floor(log10(Base)); 01684 Base = Base/pow(10.0,Expo); // separate first significant digit 01685 if(Base < 3.5) { // use only 1, 2 and 5, which ever is best fitted 01686 if(Base < 1.5) Base = 1.0; 01687 else Base = 2.0; 01688 } 01689 else { 01690 if(Base < 7.5) Base = 5.0; 01691 else { Base = 1.0; Expo++; } 01692 } 01693 GridStep = Base * pow(10.0,Expo); // grid distance in real coordinates 01694 corr = floor((Axis->up-Axis->low)/GridStep - numGrids); 01695 if(corr < 0.0) corr++; 01696 numGrids += corr; // correct rounding faults 01697 01698 01699 // upper y boundery ........................... 01700 zD = fabs(fmod(Axis->up, GridStep));// expand grid to upper diagram edge ? 01701 GridNum = zD/GridStep; 01702 if((1.0-GridNum) < 1e-10) GridNum = 0.0; // fix rounding errors 01703 if(Axis->up <= 0.0) { 01704 if(GridNum < 0.3) { Axis->up += zD; zD = 0.0; } 01705 } 01706 else if(GridNum > 0.7) Axis->up += GridStep-zD; 01707 else if(GridNum < 0.1) 01708 if(GridNum*Dist >= 1.0)// more than 1 pixel above ? 01709 Axis->up += 0.3*GridStep; // beauty correction 01710 01711 01712 // lower y boundery ........................... 01713 zD = fabs(fmod(Axis->low, GridStep));// expand grid to lower diagram edge ? 01714 GridNum = zD/GridStep; 01715 if((1.0-GridNum) < 1e-10) zD = GridNum = 0.0; // fix rounding errors 01716 if(Axis->low <= 0.0) { 01717 if(GridNum > 0.7) { Axis->low -= GridStep-zD; zD = 0.0; } 01718 else if(GridNum < 0.1) 01719 if(GridNum*Dist >= 1.0) { // more than 1 pixel above ? 01720 Axis->low -= 0.3*GridStep; // beauty correction 01721 zD += 0.3*GridStep; 01722 } 01723 } 01724 else { 01725 if(GridNum > 0.3) { 01726 zD = GridStep-zD; 01727 if(GridNum > 0.9) { 01728 if((1.0-GridNum)*Dist >= 1.0) { // more than 1 pixel above ? 01729 Axis->low -= 0.3*GridStep; // beauty correction 01730 zD += 0.3*GridStep; 01731 } 01732 } 01733 } 01734 else { Axis->low -= zD; zD = 0.0; } 01735 } 01736 01737 GridNum = Axis->low + zD; 01738 zD /= (Axis->up-Axis->low)/Dist; 01739 } 01740 else { // user defined limits 01741 zD = 0.0; 01742 Axis->low = GridNum = Axis->limit_min; 01743 Axis->up = Axis->limit_max; 01744 if(Axis->limit_max < Axis->limit_min) 01745 back = true; 01746 GridStep = Axis->step; 01747 } 01748 01749 zDstep = GridStep/(Axis->up-Axis->low)*Dist; // grid in pixel 01750 01751 if(fabs(zDstep) < 2.0) { // if grid too small, then no grid 01752 zDstep = Dist; 01753 GridStep = Axis->step = Axis->up-Axis->low; 01754 } 01755 01756 return back; 01757 } 01758 01774 bool Diagram::calcAxisLogScale(Axis *Axis, int& z, double& zD, 01775 double& zDstep, double& corr, int len) 01776 { 01777 if(fabs(Axis->max-Axis->min) < 1e-200) { // if max = min, double difference 01778 Axis->max *= 10.0; 01779 Axis->min /= 10.0; 01780 } 01781 Axis->low = Axis->min; Axis->up = Axis->max; 01782 01783 if(!Axis->autoScale) { 01784 Axis->low = Axis->limit_min; 01785 Axis->up = Axis->limit_max; 01786 } 01787 01788 01789 bool mirror=false, mirror2=false; 01790 double tmp; 01791 if(Axis->up < 0.0) { // for negative values 01792 tmp = Axis->low; 01793 Axis->low = -Axis->up; 01794 Axis->up = -tmp; 01795 mirror = true; 01796 } 01797 01798 double Base, Expo; 01799 if(Axis->autoScale) { 01800 if(mirror) { // set back values ? 01801 tmp = Axis->min; 01802 Axis->min = -Axis->max; 01803 Axis->max = -tmp; 01804 } 01805 01806 Expo = floor(log10(Axis->max)); 01807 Base = Axis->max/pow(10.0,Expo); 01808 if(Base > 3.0001) Axis->up = pow(10.0,Expo+1.0); 01809 else if(Base < 1.0001) Axis->up = pow(10.0,Expo); 01810 else Axis->up = 3.0 * pow(10.0,Expo); 01811 01812 Expo = floor(log10(Axis->min)); 01813 Base = Axis->min/pow(10.0,Expo); 01814 if(Base < 2.999) Axis->low = pow(10.0,Expo); 01815 else if(Base > 9.999) Axis->low = pow(10.0,Expo+1.0); 01816 else Axis->low = 3.0 * pow(10.0,Expo); 01817 01818 corr = double(len) / log10(Axis->up / Axis->low); 01819 01820 z = 0; 01821 zD = Axis->low; 01822 zDstep = pow(10.0,Expo); 01823 01824 if(mirror) { // set back values ? 01825 tmp = Axis->min; 01826 Axis->min = -Axis->max; 01827 Axis->max = -tmp; 01828 } 01829 } 01830 else { // user defined limits 01831 if(Axis->up < Axis->low) { 01832 tmp = Axis->low; 01833 Axis->low = Axis->up; 01834 Axis->up = tmp; 01835 mirror2 = true; 01836 } 01837 01838 Expo = floor(log10(Axis->low)); 01839 Base = ceil(Axis->low/pow(10.0,Expo)); 01840 zD = Base * pow(10.0, Expo); 01841 zDstep = pow(10.0,Expo); 01842 if(zD > 9.5*zDstep) zDstep *= 10.0; 01843 01844 corr = double(len) / log10(Axis->up / Axis->low); 01845 z = int(corr*log10(zD / Axis->low) + 0.5); // int(..) implies floor(..) 01846 01847 if(mirror2) { // set back values ? 01848 tmp = Axis->low; 01849 Axis->low = Axis->up; 01850 Axis->up = tmp; 01851 } 01852 } 01853 01854 if(mirror) { // set back values ? 01855 tmp = Axis->low; 01856 Axis->low = -Axis->up; 01857 Axis->up = -tmp; 01858 } 01859 01860 if(mirror == mirror2) return false; 01861 else return true; 01862 } 01863 01864 // -------------------------------------------------------------- 01865 bool Diagram::calcYAxis(Axis *Axis, int x0) 01866 { 01867 int z, w; 01868 double GridStep, corr, zD, zDstep, GridNum; 01869 01870 QString tmp; 01871 // get size of text using the screen-compatible metric 01872 QFontMetrics metrics(QucsSettings.font, 0); 01873 int maxWidth = 0; 01874 01875 bool back = false; 01876 if(Axis->log) { 01877 if(Axis->autoScale) { 01878 if(Axis->max*Axis->min <= 0.0) return false; // invalid 01879 } 01880 else if(Axis->limit_min*Axis->limit_max <= 0.0) return false; // invalid 01881 01882 back = calcAxisLogScale(Axis, z, zD, zDstep, corr, y2); 01883 01884 if(back) z = y2; 01885 while((z <= y2) && (z >= 0)) { // create all grid lines 01886 if(Axis->GridOn) if(z < y2) if(z > 0) 01887 Lines.prepend(new Line(0, z, x2, z, GridPen)); // y grid 01888 01889 if((zD < 1.5*zDstep) || (z == 0)) { 01890 tmp = misc::StringNiceNum(zD); 01891 if(Axis->up < 0.0) tmp = '-'+tmp; 01892 01893 w = metrics.width(tmp); // width of text 01894 if(maxWidth < w) maxWidth = w; 01895 if(x0 > 0) 01896 Texts.append(new Text(x0+7, z-6, tmp)); // text aligned left 01897 else 01898 Texts.append(new Text(-w-7, z-6, tmp)); // text aligned right 01899 01900 // y marks 01901 Lines.append(new Line(x0-5, z, x0+5, z, QPen(Qt::black,0))); 01902 } 01903 01904 zD += zDstep; 01905 if(zD > 9.5*zDstep) zDstep *= 10.0; 01906 if(back) { 01907 z = int(corr*log10(zD / fabs(Axis->up)) + 0.5); // int() implies floor() 01908 z = y2 - z; 01909 } 01910 else 01911 z = int(corr*log10(zD / fabs(Axis->low)) + 0.5);// int() implies floor() 01912 } 01913 } 01914 else { // not logarithmical 01915 back = calcAxisScale(Axis, GridNum, zD, zDstep, GridStep, double(y2)); 01916 01917 double Expo; 01918 if(Axis->up == 0.0) Expo = log10(fabs(Axis->up-Axis->low)); 01919 else Expo = log10(fabs(Axis->up)); 01920 01921 zD += 0.5; // perform rounding 01922 z = int(zD); // "int(...)" implies "floor(...)" 01923 while((z <= y2) && (z >= 0)) { // create all grid lines 01924 if(fabs(GridNum) < 0.01*pow(10.0, Expo)) GridNum = 0.0;// make 0 really 0 01925 tmp = misc::StringNiceNum(GridNum); 01926 01927 w = metrics.width(tmp); // width of text 01928 if(maxWidth < w) maxWidth = w; 01929 if(x0 > 0) 01930 Texts.append(new Text(x0+8, z-6, tmp)); // text aligned left 01931 else 01932 Texts.append(new Text(-w-7, z-6, tmp)); // text aligned right 01933 GridNum += GridStep; 01934 01935 if(Axis->GridOn) if(z < y2) if(z > 0) 01936 Lines.prepend(new Line(0, z, x2, z, GridPen)); // y grid 01937 Lines.append(new Line(x0-5, z, x0+5, z, QPen(Qt::black,0))); // y marks 01938 zD += zDstep; 01939 z = int(zD); 01940 } 01941 } // of "if(ylog) ... else ..." 01942 if(x0 == 0) x1 = maxWidth+14; 01943 else x3 = x2+maxWidth+14; 01944 return true; 01945 } 01946 01947 // convenience wrappers 01948 bool Diagram::insideDiagramP(Graph::iterator const& p) const 01949 { 01950 float f1 = p->getScrX(); 01951 float f2 = p->getScrY(); 01952 return insideDiagram(f1,f2); 01953 } 01954 void Diagram::calcCoordinateP (const double*x, const double*y, const double*z, Graph::iterator& p, Axis const* A) const 01955 { 01956 float f1, f2; 01957 calcCoordinate(x, y, z, &f1, &f2, A); 01958 p->setScr(f1, f2); 01959 }; 01960 01961 01962 // vim:ts=8:sw=2:noet