Qucs-GUI  0.0.19
/home/travis/build/Qucs/qucs/qucs/qucs/diagrams/diagram.cpp
Go to the documentation of this file.
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
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines