Qucs-GUI  0.0.19
/home/travis/build/Qucs/qucs/qucs/qucs/diagrams/rect3ddiagram.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               rect3ddiagram.cpp
00003                              -------------------
00004     begin                : Sat Mar 5 2005
00005     copyright            : (C) 2005 by Michael Margraf
00006     email                : michael.margraf@alumni.tu-berlin.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00023 #include <QFontMetrics>
00024 
00025 #if HAVE_CONFIG_H
00026 # include <config.h>
00027 #endif
00028 #include <stdlib.h>
00029 #include <cmath>
00030 #include <float.h>
00031 #include <limits.h>
00032 #if HAVE_IEEEFP_H
00033 # include <ieeefp.h>
00034 #endif
00035 
00036 #include "rect3ddiagram.h"
00037 #include "main.h"
00038 #include "misc.h"
00039 
00040 Rect3DDiagram::Rect3DDiagram(int _cx, int _cy) : Diagram(_cx, _cy)
00041 {
00042   x1 = 10;     // position of label text
00043   y1 = y3 = 7;
00044   x2 = 200;    // initial size of diagram
00045   y2 = 200;
00046   x3 = 207;    // with some distance for right axes text
00047 
00048   Mem = pMem = 0;  // auxiliary buffer for hidden lines
00049 
00050   Name = "Rect3D"; // BUG
00051   // symbolic diagram painting
00052   Lines.append(new Line(0, 0, cx,  0, QPen(Qt::black,0)));
00053   Lines.append(new Line(0, 0,  0, cy, QPen(Qt::black,0)));
00054   Lines.append(new Line(0, 0, cx/2, cy/2, QPen(Qt::black,0)));
00055 }
00056 
00057 Rect3DDiagram::~Rect3DDiagram()
00058 {
00059 }
00060 
00061 // ------------------------------------------------------------
00062 // Calculates the coefficients for 3D -> 2D transformation
00063 void Rect3DDiagram::calcCoefficients()
00064 {
00065   double rX = double(rotX) * pi/180.0;
00066   double rY = double(rotY) * pi/180.0;
00067   double rZ = double(rotZ) * pi/180.0;
00068   
00069   cxy = sin(rZ);  cxx = cos(rZ);
00070   cxz = sin(rY);  rY  = cos(rY);
00071   cyz = sin(rX);  czz = cos(rX);
00072   rX  = cyz*cxz;  rZ = czz*cxz;
00073   cyx = czz * cxy + rX * cxx;
00074   cyy = czz * cxx - rX * cxy;
00075   czx = cyz * cxy - rZ * cxx;
00076   czy = cyz * cxx + rZ * cxy;
00077   cxx *=  rY;  cxy *= -rY;  cyz *= -rY;  czz *=  rY;
00078 }
00079 
00080 // ------------------------------------------------------------
00081 double Rect3DDiagram::calcX_2D(double x, double y, double z) const
00082 {
00083   return (cxx * x + cxy * y + cxz * z) * scaleX;
00084 }
00085 
00086 // ------------------------------------------------------------
00087 double Rect3DDiagram::calcY_2D(double x, double y, double z) const
00088 {
00089   return (cyx * x + cyy * y + cyz * z) * scaleY;
00090 }
00091 
00092 // ------------------------------------------------------------
00093 double Rect3DDiagram::calcZ_2D(double x, double y, double z) const
00094 {
00095   return czx * x + czy * y + czz * z;
00096 }
00097 
00098 // ------------------------------------------------------------
00099 // Determines the position of the coordinate cross, i.e. calculates
00100 // "scaleX", "scaleY", "xorig" and "yorig". Returns the corner with
00101 // the largest distance from the viewer.
00102 int Rect3DDiagram::calcCross(int *Xses, int *Yses)
00103 {
00104   double x3D, y3D, z3D, x2D[8], y2D[8], z2D;
00105   double XMIN_2D, XMAX_2D, YMIN_2D, YMAX_2D, ZMIN_2D;
00106 
00107   int z, Center = 0;  // used to save minimum z (is center for axis cross)
00108   scaleX = scaleY = 1.0;  // used in "calcX_2D" and "calcY_2D"
00109   XMIN_2D = YMIN_2D = XMAX_2D = YMAX_2D = ZMIN_2D = 0.0; // origin is zero
00110   for(z=0; z<8; z++) {  // check 2D coordinates of all 8 quadrat corners
00111     if(z & 1) x3D = 1.0;  else x3D = 0.0;
00112     if(z & 2) y3D = 1.0;  else y3D = 0.0;
00113     if(z & 4) z3D = 1.0;  else z3D = 0.0;
00114     x2D[z] = calcX_2D(x3D, y3D, z3D);
00115     y2D[z] = calcY_2D(x3D, y3D, z3D);
00116     z2D = calcZ_2D(x3D, y3D, z3D);
00117 
00118     if(x2D[z] < XMIN_2D)  XMIN_2D = x2D[z];
00119     if(x2D[z] > XMAX_2D)  XMAX_2D = x2D[z];
00120     if(y2D[z] < YMIN_2D)  YMIN_2D = y2D[z];
00121     if(y2D[z] > YMAX_2D)  YMAX_2D = y2D[z];
00122     if(z2D < ZMIN_2D) { ZMIN_2D = z2D; Center = z; }
00123   }
00124 
00125   scaleX = double(x2) / (XMAX_2D - XMIN_2D); // scaling 3D -> 2D transformation
00126   scaleY = double(y2) / (YMAX_2D - YMIN_2D);
00127   xorig  = -XMIN_2D * scaleX;   // position of origin
00128   yorig  = -YMIN_2D * scaleY;
00129 
00130   for(z=0; z<8; z++) {  // calculate 2D coordinates of all corners
00131     *(Xses+z) = int(x2D[z] * scaleX + 0.5 + xorig);
00132     *(Yses+z) = int(y2D[z] * scaleY + 0.5 + yorig);
00133   }
00134   return Center;
00135 }
00136 
00137 // ------------------------------------------------------------
00138 // Is needed for markers.
00139 void Rect3DDiagram::calcCoordinate(const double* xD, const double* zD, const double* yD,
00140                                    float *px, float *py, Axis const*) const
00141 {
00142   double x3D = zD[0];
00143   double y3D = zD[1];
00144   double z3D;
00145   if(zAxis.log) {
00146     z3D = sqrt(x3D*x3D + y3D*y3D);
00147 /*    if(z3D <= 0.0)  clipping not yet correct implemented
00148       z3D = -1e5;   // "negative infinity"
00149     else*/
00150       z3D = log10(z3D / fabs(zAxis.low)) / log10(zAxis.up / zAxis.low);
00151   }
00152   else {
00153     if(fabs(y3D) > 1e-250) // preserve negative values if no complex number
00154       x3D = sqrt(x3D*x3D + y3D*y3D);
00155     z3D = (x3D - zAxis.low) / (zAxis.up - zAxis.low);
00156   }
00157 
00158   x3D = *(xD++);
00159   if(xAxis.log) {
00160     x3D /= xAxis.low;
00161 /*    if(x3D <= 0.0)  clipping not yet correct implemented
00162       x3D = -1e5;   // "negative infinity"
00163     else*/
00164       x3D = log10(x3D) / log10(xAxis.up / xAxis.low);
00165   }
00166   else
00167     x3D = (x3D - xAxis.low) / (xAxis.up - xAxis.low);
00168 
00169   if(yAxis.log) {
00170     y3D = (*yD) / yAxis.low;
00171 /*    if(y3D <= 0.0)  clipping not yet correct implemented
00172       y3D = -1e5;   // "negative infinity"
00173     else*/
00174       y3D = log10(y3D) / log10(yAxis.up / yAxis.low);
00175   }
00176   else
00177     y3D = (*yD - yAxis.low) / (yAxis.up - yAxis.low);
00178 
00179   *px = float(calcX_2D(x3D, y3D, z3D)) + xorig;
00180   *py = float(calcY_2D(x3D, y3D, z3D)) + yorig;
00181 
00182   if(std::isfinite(*px))
00183     if(std::isfinite(*py))
00184       return;
00185 
00186   *px = float(xorig);
00187   *py = float(yorig);
00188 }
00189 
00190 // --------------------------------------------------------------
00191 void Rect3DDiagram::finishMarkerCoordinates(float& fCX, float& fCY) const
00192 {
00193   if(!insideDiagram(fCX, fCY)) {
00194     fCX = fCY = 0.0;
00195   }
00196 }
00197 
00198 // ------------------------------------------------------------
00199 void Rect3DDiagram::calcCoordinate3D(double x, double y, double zr, double zi,
00200           tPoint3D *p, tPointZ *pz)
00201 {
00202   if(zAxis.log) {
00203     zr = sqrt(zr*zr + zi*zi);
00204 /*    if(zr <= 0.0)  clipping not yet correct implemented
00205       zr = -1e5;   // "negative infinity"
00206     else*/
00207       zr = log10(zr / fabs(zAxis.low)) / log10(zAxis.up / zAxis.low);
00208   }
00209   else {
00210     if(fabs(zi) > 1e-250) // preserve negative values if no complex number
00211       zr = sqrt(zr*zr + zi*zi);
00212     zr = (zr - zAxis.low) / (zAxis.up - zAxis.low);
00213   }
00214 
00215   if(xAxis.log) {
00216     x /= xAxis.low;
00217 /*    if(x <= 0.0)  clipping not yet correct implemented
00218       x = -1e5;   // "negative infinity"
00219     else*/
00220       x = log10(x) / log10(xAxis.up / xAxis.low);
00221   }
00222   else
00223     x = (x - xAxis.low) / (xAxis.up - xAxis.low);
00224 
00225   if(yAxis.log) {
00226     y = y / yAxis.low;
00227 /*    if(y <= 0.0)  clipping not yet correct implemented
00228       y = -1e5;   // "negative infinity"
00229     else*/
00230       y = log10(y) / log10(yAxis.up / yAxis.low);
00231   }
00232   else
00233     y = (y - yAxis.low) / (yAxis.up - yAxis.low);
00234 
00235   p->x  = int(calcX_2D(x, y, zr) + 0.5 + xorig);
00236   p->y  = int(calcY_2D(x, y, zr) + 0.5 + yorig);
00237   p->No = pz->No = p-Mem;
00238   p->done = 0;
00239   pz->z = float(calcZ_2D(x, y, zr));
00240 }
00241 
00242 // --------------------------------------------------------------
00243 bool Rect3DDiagram::isHidden(int x, int y, tBound *Bounds, char *zBuffer)
00244 {
00245   // remember the boundings of the polygon
00246   if( (Bounds+x)->max < y )  (Bounds+x)->max = y;
00247   if( (Bounds+x)->min > y )  (Bounds+x)->min = y;
00248 
00249   // diagram area already used ?
00250   return ( *(zBuffer + (y>>3) + x * ((y2+7)>>3)) & (1 << (y & 7)) ) != 0;
00251 }
00252 
00253 // --------------------------------------------------------------
00254 // Enlarge memory block if neccessary.
00255 void Rect3DDiagram::enlargeMemoryBlock(tPoint3D* &MemEnd)
00256 {
00257   if(pMem >= MemEnd) {
00258     int Size = MemEnd - Mem + 256;
00259     MemEnd = Mem;
00260     Mem  = (tPoint3D*)realloc(Mem, Size*sizeof(tPoint3D));
00261     pMem += Mem - MemEnd;
00262     MemEnd = Mem + Size - 5;
00263   }
00264 }
00265 
00266 // --------------------------------------------------------------
00267 // Calculate all 2D points of the line between point "p" and "p+1".
00268 // Parameters:   p        - pointer on 3D coordinate of line start point
00269 //                          (p+1 points onto line end point)
00270 //               MemEnd   - pointer where memory block ends
00271 //               Bounds   - memory block for occupied polygon area
00272 //               zBuffer  - memory block for occupied diagram area
00273 void Rect3DDiagram::calcLine(tPoint3D* &p, tPoint3D* &MemEnd,
00274         tBound *Bounds, char *zBuffer)
00275 {
00276   int Pos_;
00277   int x1_ = p->x, y1_ = p->y;
00278   int x2_ = (p+1)->x, y2_ = (p+1)->y;
00279 
00280   bool wasHidden = isHidden(x1_, y1_, Bounds, zBuffer);
00281   if(wasHidden)
00282     if((p->done & 1) == 0)
00283       p->done |= 4;   // mark as hidden
00284 
00285   int ax_ = 0, ay_ = 0;
00286   int ix_, iy_, dx_, dy_, of_;
00287 
00288   if(x2_ >= x1_) {
00289     dx_ = x2_ - x1_;
00290     ix_ = 1;
00291   }
00292   else {
00293     dx_ = x1_ - x2_;
00294     ix_ = -1;
00295   }
00296 
00297   if(y2_ >= y1_) {
00298     dy_ = y2_ - y1_;
00299     iy_ = 1;
00300   }
00301   else {
00302     dy_ = y1_ - y2_;
00303     iy_ = -1;
00304   }
00305 
00306   if(dx_ < dy_) {
00307     of_ = dx_;   // exchange dx and dy
00308     dx_ = dy_;
00309     dy_ = of_;
00310 
00311     ax_ = iy_;
00312     ay_ = ix_;
00313     ix_ = iy_ = 0;
00314   }
00315 
00316   of_ = dx_ >> 1;
00317   for(int i=dx_; i>1; i--) {   // calculate each point of the line
00318     x1_ += ix_;
00319     y1_ += ax_;
00320     of_ += dy_;
00321     if(of_ > dx_) {
00322       of_ -= dx_;
00323       x1_ += ay_;
00324       y1_ += iy_;
00325     }
00326     
00327     if( isHidden(x1_, y1_, Bounds, zBuffer) != wasHidden )
00328       if((p->done & 1) == 0) {
00329         wasHidden = !wasHidden;
00330         pMem->x  = x1_;
00331         pMem->y  = y1_;
00332         pMem->No = p->No;
00333         pMem->done = 0;
00334         if(wasHidden) pMem->done = 4;   // mark as hidden
00335         pMem++;
00336 
00337         Pos_ = p - Mem;
00338         // Enlarge memory block if neccessary.
00339         enlargeMemoryBlock(MemEnd);   // this may make "p" invalid (realloc)
00340         p = Mem + Pos_;  // rebuild "p"
00341     }
00342   }
00343 
00344   // extra treatment for last point (create no further point)
00345   if(isHidden((p+1)->x, (p+1)->y, Bounds, zBuffer))
00346     if(((p+1)->done & 1) == 0)
00347       (p+1)->done |= 4;   // mark as hidden
00348 
00349   p->done |= 1;   // mark as already worked on
00350 }
00351 
00352 // --------------------------------------------------------------
00353 // Compare functions for GNU qsort routine.
00354 int Rect3DDiagram::comparePoint3D(const void *Point1, const void *Point2)
00355 {
00356   return ((tPoint3D*)Point1)->No - ((tPoint3D*)Point2)->No;
00357 }
00358 int Rect3DDiagram::comparePointZ(const void *Point1, const void *Point2)
00359 {
00360   if((((tPointZ*)Point2)->z - ((tPointZ*)Point1)->z) < 0.0f)
00361     return -1;
00362   return 1;
00363 }
00364 
00365 // --------------------------------------------------------------
00366 // Removes the invisible parts of the graph.
00367 void Rect3DDiagram::removeHiddenLines(char *zBuffer, tBound *Bounds)
00368 {
00369   double Dummy = 0.0;  // number for 1-dimensional data in 3D cartesian
00370   double *px, *py, *pz;
00371 
00372   tPoint3D *p;
00373   int i, j, z, dx, dy, Size=0;
00374   // pre-calculate buffer size to avoid reallocations in the first step
00375   foreach(Graph *g, Graphs)
00376     if(g->cPointsY)
00377       Size += g->axis(0)->count * g->countY;
00378 
00379   // "Mem" should be the last malloc to simplify realloc
00380   tPointZ *zMem = (tPointZ*)malloc( (Size+2)*sizeof(tPointZ) );
00381   Mem  = (tPoint3D*)malloc( 2*(Size+2)*sizeof(tPoint3D) );
00382 
00383   pMem = Mem;
00384   tPointZ *zp = zMem, *zp_tmp;
00385 
00386   // ...............................................................
00387   foreach(Graph *g, Graphs) {
00388 
00389     pz = g->cPointsY;
00390     if(!pz) continue;
00391     if(g->numAxes() < 1) continue;
00392 
00393     py = &Dummy;
00394     if(g->countY > 1)  py = g->axis(1)->Points;
00395 
00396     p = pMem;  // save status for cross grid
00397     zp_tmp = zp;
00398     // ..........................................
00399     // calculate coordinates of all lines
00400     dx = g->axis(0)->count;
00401     if(g->countY > 1)  dy = g->axis(1)->count;
00402     else  dy = 0;
00403     for(i=g->countY-1; i>=0; i--) {   // y coordinates
00404       px = g->axis(0)->Points;
00405     
00406       for(j=dx; j>0; j--) { // x coordinates
00407         calcCoordinate3D(*(px++), *py, *pz, *(pz+1), pMem++, zp++);
00408         pz += 2;
00409       }
00410 
00411       (pMem-1)->done |= 8;  // mark as "last in line"
00412       py++;
00413       if(dy > 0) if((i % dy) == 0)
00414         py = g->axis(1)->Points;
00415     }
00416     (pMem-1)->done |= 512;  // mark as "last point before grid"
00417 
00418     // ..........................................
00419     // copy points for cross lines ("dx", "dy" still unchanged ! )
00420     if(g->countY > 1) {
00421       zp = zp_tmp;
00422       for(j=g->countY/dy; j>0; j--) { // every plane
00423         for(i=dx; i>0; i--) {  // every branch
00424           for(z=dy; z>0; z--) {  // every point
00425             pMem->x  = p->x;
00426             pMem->y  = p->y;
00427             pMem->No = pMem-Mem;
00428             pMem->done = 0;
00429             zp->NoCross = pMem-Mem; // position of its cross grid
00430             pMem++;
00431             p += dx;  // next coordinate
00432             zp += dx;
00433           }
00434           (pMem-1)->done |= 8;  // mark as "last in line"
00435           p  -= dx*dy - 1;  // next z coordinate
00436           zp -= dx*dy - 1;
00437         }
00438         p  += dx*(dy-1);
00439         zp += dx*(dy-1);
00440       }
00441     }
00442     (pMem-1)->done |= 256;  // mark as "very last point"
00443 
00444 
00445     if(hideLines) {
00446     // ..........................................
00447     // Calculate the z-coordinate of all polygons by building the
00448     // sum of the z-coordinates of all of its 4 corners.
00449     // After this, each point represents one polygon. The unneccessary
00450     // points are filled with "-FLTMAX".
00451     zp = zp_tmp;
00452     // "dx" and "dy" are still unchanged !
00453     for(i=g->countY-1; i>=0; i--) {   // all branches
00454       if(dy > 0) if(i % dy) {
00455         for(j=dx-1; j>0; j--) {   // x coordinates
00456           zp->z += (zp+1)->z + (zp+dx)->z + (zp+dx+1)->z;
00457           zp++;
00458         }
00459         zp->z = -FLT_MAX;  // last one not needed
00460         zp++;
00461         continue;
00462       }
00463 
00464       // last line not needed
00465       for(j=dx; j>0; j--) {   // x coordinates
00466         zp->z = -FLT_MAX;  // last one not needed
00467         zp++;
00468       }
00469     }
00470     }  // of "if(hideLines)"
00471 
00472   }  // of "for(Graphs)"
00473 
00474 
00475   if(!hideLines) {  // do not hide invisible lines
00476     free(zMem);
00477     return;
00478   }
00479 
00480 #if 0
00481   qDebug("##########################################");
00482   qDebug("Size 1b: Size=%d, %d, %d", Size, pMem-Mem, zp-zMem);
00483   for(tPoint3D *p=Mem; p<pMem; p++)
00484     qDebug("xyPoints:  %d/%d - %d - %d", p->x, p->y, p->No, p->done);
00485   qDebug("------------------------------------------");
00486   for(tPointZ *p=zMem; p-zMem<Size; p++)
00487     qDebug("zPoints:   %g - %d", p->z, p->No);
00488 #endif
00489 
00490 
00491   // ..........................................
00492   // Sort z-coordinates (greatest first).
00493   // After this the polygons that have the smallest distance to the
00494   // viewer are on top of the list and thus, will be processed first.
00495   qsort(zMem, Size, sizeof(tPointZ), comparePointZ);
00496 
00497 #if 0
00498   qDebug("--------------------------- z sorting");
00499   for(tPointZ *p=zMem; p-zMem<Size; p++)
00500     qDebug("zPoints:   %g - %d", p->z, p->No);
00501 #endif
00502 
00503 
00504   // ..........................................
00505   char *pc;
00506   tPoint3D *MemEnd = Mem + 2*Size - 5;   // limit of buffer
00507 
00508   zp = zMem;
00509   foreach(Graph *g, Graphs) {
00510     if(!g->cPointsY) continue;
00511     dx = g->axis(0)->count;
00512     if(g->countY > 1)  dy = g->axis(1)->count;
00513     else  dy = 1;
00514 
00515     // look for hidden lines ...
00516     for(int No = g->countY/dy * (dx-1)*(dy-1); No>0; No--) {
00517 
00518       // reset the polygon bounding buffer
00519       for(i=x2; i>=0; i--) {
00520         (Bounds+i)->max = INT_MIN;
00521         (Bounds+i)->min = INT_MAX;
00522       }
00523 
00524       // work on all 4 lines of polygon
00525       p = Mem + zp->No;  // polygon corner coordinates
00526       calcLine(p, MemEnd, Bounds, zBuffer);
00527 
00528       p += dx;
00529       calcLine(p, MemEnd, Bounds, zBuffer);
00530 
00531       p = Mem + zp->NoCross;  // cross grid
00532       calcLine(p, MemEnd, Bounds, zBuffer);
00533 
00534       p += dy;
00535       calcLine(p, MemEnd, Bounds, zBuffer);
00536 
00537       // mark the area of the polygon (stored in "*Bounds") as used
00538       for(i=x2-1; i>=0; i--)  // all x coordinates
00539         if( (Bounds+i)->max > INT_MIN) {
00540           pc = zBuffer + i * ((y2+7)>>3);
00541           for(j=(Bounds+i)->min; j<=(Bounds+i)->max; j++) // all y coordinates
00542             *(pc + (j>>3)) |= (1 << (j & 7));
00543         }
00544 
00545       zp++;   // next polygon
00546     }
00547 
00548   }  // of "for(Graphs)"
00549 
00550 #if 0
00551   qDebug("--------------------------- hidden lines %d", pMem-Mem);
00552   for(tPoint3D *p=Mem; p<pMem; p++)
00553     qDebug("xyPoints:  %d/%d - %d - %d", p->x, p->y, p->No, p->done);
00554 #endif
00555 
00556   free(zMem);
00557 
00558   // sort "No" (least one first)
00559   qsort(Mem, pMem - Mem, sizeof(tPoint3D), comparePoint3D);
00560 
00561 #if 0
00562   qDebug("--------------------------- last sorting %d", pMem-Mem);
00563   for(tPoint3D *p=Mem; p<pMem; p++)
00564     qDebug("xyPoints:  %d/%d - %d - %d", p->x, p->y, p->No, p->done);
00565   qDebug("\n");
00566 #endif
00567 }
00568 
00569 // --------------------------------------------------------------
00570 // Removes the invisible parts of the coordinate cross.
00571 void Rect3DDiagram::removeHiddenCross(int x1_, int y1_, int x2_, int y2_,
00572                                       char *zBuffer, tBound *Bounds)
00573 {
00574   pMem = Mem;
00575 
00576   pMem->x  = x1_;
00577   pMem->y  = y1_;
00578   pMem->No = 0;
00579   pMem->done = 0;
00580   pMem++;
00581 
00582   pMem->x  = x2_;
00583   pMem->y  = y2_;
00584   pMem->No = 1;
00585   pMem->done = 0;
00586   pMem++;
00587 
00588   tPoint3D *p = Mem+6;
00589   calcLine(Mem, p, Bounds, zBuffer);
00590   *pMem = *(Mem+1);
00591   *(Mem+1) = *Mem;
00592   p = Mem+2;
00593   do {
00594     if(((p-1)->done & 4) == 0)
00595       Lines.append(new Line((p-1)->x, (p-1)->y, p->x, p->y, QPen(Qt::black,0)));
00596     p++;
00597   } while(p <= pMem);
00598 }
00599 
00600 // --------------------------------------------------------------
00601 void Rect3DDiagram::calcLimits()
00602 {
00603   int i;
00604   double a, b, c;
00605 
00606   if(xAxis.autoScale) {// check before, to preserve limit exchange (max < min)
00607     if(xAxis.log) {
00608       calcAxisLogScale(&xAxis, i, a, b, c, x2);
00609       xAxis.step = 1.0;
00610     }
00611     else  calcAxisScale(&xAxis, a, b, c, xAxis.step, double(x2));
00612     xAxis.limit_min = xAxis.low;
00613     xAxis.limit_max = xAxis.up;
00614   }
00615 
00616   if(yAxis.autoScale) {// check before, to preserve limit exchange (max < min)
00617     if(yAxis.log) {
00618       calcAxisLogScale(&yAxis, i, a, b, c, y2);
00619       yAxis.step = 1.0;
00620     }
00621     else  calcAxisScale(&yAxis, a, b, c, yAxis.step, double(y2));
00622     yAxis.limit_min = yAxis.low;
00623     yAxis.limit_max = yAxis.up;
00624   }
00625 
00626   if(zAxis.autoScale) {// check before, to preserve limit exchange (max < min)
00627     if(zAxis.log) {
00628       calcAxisLogScale(&zAxis, i, a, b, c, y2);
00629       zAxis.step = 1.0;
00630     }
00631     else  calcAxisScale(&zAxis, a, b, c, zAxis.step, double(y2));
00632     zAxis.limit_min = zAxis.low;
00633     zAxis.limit_max = zAxis.up;
00634   }
00635 }
00636 
00637 // --------------------------------------------------------------
00638 int Rect3DDiagram::calcAxis(Axis *Axis, int x, int y,
00639                             double xD, double phi, bool Right)
00640 {
00641   Q_UNUSED(Right);
00642 
00643   double GridStep, corr, yD, stepD, GridNum, Expo;
00644   double xstepD, ystepD;
00645 
00646   QString tmp;
00647   // get size of text using the screen-compatible metric
00648   QFontMetrics metrics(QucsSettings.font, 0);
00649   int maxWidth = 0;
00650   int count, gx, gy, w;
00651 
00652   if(phi > 0.0)  Expo = phi - pi/2.0;
00653   else  Expo = phi + pi/2.0;
00654   gx = int(5.4 * cos(Expo) + 0.5);  // short grid marker lines
00655   gy = int(5.4 * sin(Expo) + 0.5);
00656 
00657   
00658   if(Axis->log) {
00659 
00660     bool back = calcAxisLogScale(Axis, w, yD, stepD, corr, int(xD));
00661 
00662     //double upD  = Axis->up;
00663     if(yD > 1.5*stepD)  yD = 10.0*stepD;  // always start at power of 10
00664     if(back) {
00665       //upD  = Axis->low;
00666       phi += pi;
00667       xD   = 0.0;
00668     }
00669 
00670     int xLen, yLen;
00671     ystepD = corr * log10(yD / fabs(Axis->low));
00672     while(ystepD <= xD) {  // create all grid lines
00673       
00674       tmp = misc::StringNiceNum(yD);
00675       if(Axis->up < 0.0)  tmp = '-'+tmp;
00676       w = metrics.width(tmp);  // width of text
00677       if(maxWidth < w) maxWidth = w;
00678       
00679       xLen = int(ystepD * cos(phi) + 0.5) + x;
00680       yLen = int(ystepD * sin(phi) + 0.5) + y;
00681       if(Qt::DockRight)
00682   Texts.append(new Text(xLen+3+gx, yLen-6+gy, tmp));
00683       else
00684   Texts.append(new Text(xLen-w-2-gx, yLen-6-gy, tmp));
00685       
00686       // short grid marks
00687       Lines.append(new Line(xLen-gx, yLen-gy, xLen+gx, yLen+gy,
00688           QPen(Qt::black,0)));
00689       yD *= 10.0;
00690       ystepD += corr;
00691     }
00692     
00693   }
00694   else {  // not logarithmical
00695     calcAxisScale(Axis, GridNum, yD, stepD, GridStep, xD);
00696     count = int((xD - yD) / stepD) + 1;  // number of grids
00697     
00698     xstepD = stepD * cos(phi);
00699     ystepD = stepD * sin(phi);
00700     xD = yD * cos(phi) + 0.5 + double(x);
00701     yD = yD * sin(phi) + 0.5 + double(y);
00702     
00703     if(Axis->up == 0.0)  Expo = log10(fabs(Axis->up-Axis->low));
00704     else  Expo = log10(fabs(Axis->up));
00705     
00706     for(; count>0; count--) {
00707       x = int(xD);
00708       y = int(yD);
00709       if(fabs(GridNum) < 0.01*pow(10.0, Expo)) GridNum = 0.0; // make 0 really 0
00710       tmp = misc::StringNiceNum(GridNum);
00711       
00712       w = metrics.width(tmp);  // width of text
00713       if(maxWidth < w) maxWidth = w;
00714       if(Qt::DockRight)
00715   Texts.append(new Text(x+3+gx, y-6+gy, tmp)); // place text right
00716       else
00717   Texts.append(new Text(x-w-2-gx, y-6-gy, tmp)); // place left
00718       GridNum += GridStep;
00719       
00720       // short grid marks
00721       Lines.append(new Line(x-gx, y-gy, x+gx, y+gy, QPen(Qt::black,0)));
00722       xD += xstepD;
00723       yD += ystepD;
00724     }
00725   } // of "if(Axis->log) ... else ..."
00726   
00727   return maxWidth+5;
00728 }
00729 
00730 // --------------------------------------------------------------
00731 void Rect3DDiagram::createAxis(Axis *Axis, bool Right,
00732                                int x1_, int y1_, int x2_, int y2_)
00733 {
00734   Q_UNUSED(Right);
00735 
00736   DataX const *pD;
00737   double phi, cos_phi, sin_phi;
00738   int x, y, z, w, valid, Index = 0;
00739   if(Axis == &yAxis)  Index = 1;
00740 
00741   QString s;
00742   // get size of text using the screen-compatible metric
00743   QFontMetrics metrics(QucsSettings.font, 0);
00744 
00745   x = x2_ - x1_;
00746   y = y2_ - y1_;
00747   cos_phi = sqrt(double(x*x) + double(y*y));
00748   phi = atan2(double(y), double(x));
00749   
00750   valid = calcAxis(Axis, x1_, y1_, cos_phi, phi, Qt::DockRight); // axis numbering
00751   z = (int)cos_phi;
00752   cos_phi = cos(phi);
00753   sin_phi = sin(phi);
00754 
00755   if(fabs(phi-1e-5) > pi/2.0) {
00756     x1_ = x2_;  cos_phi *= -1;
00757     y1_ = y2_;  sin_phi *= -1;
00758   }
00759   x = x1_ + int(double(valid)*sin_phi);
00760   y = y1_ - int(double(valid)*cos_phi);
00761   if(Axis->Label.isEmpty()) {
00762     // write all labels ----------------------------------------
00763     foreach(Graph *pg, Graphs) {
00764       if(Axis != &zAxis) {
00765         if(!pg->cPointsY)  continue;
00766         if(valid < 0) {
00767           delete[] pg->cPointsY;
00768           pg->cPointsY = 0;
00769           continue;
00770         }
00771         pD = pg->axis(Index);
00772         if(!pD) continue;
00773         s = pD->Var;
00774       }
00775       else {
00776         s = pg->Var;
00777         if(!pg->cPointsY)  s += INVALID_STR;
00778       }
00779       x += int(double(metrics.lineSpacing())*sin_phi);
00780       y -= int(double(metrics.lineSpacing())*cos_phi);
00781       w = metrics.width(s);
00782       Texts.append(new Text(x+int(double((z-w)>>1)*cos_phi),
00783                             y+int(double((z-w)>>1)*sin_phi),
00784                             s, pg->Color, 12.0, cos_phi, sin_phi));
00785     }
00786   }
00787   else {
00788     x += int(double(metrics.lineSpacing())*sin_phi);
00789     y -= int(double(metrics.lineSpacing())*cos_phi);
00790     w = metrics.width(Axis->Label);
00791     Texts.append(new Text(x+int(double((z-w)>>1)*cos_phi),
00792                           y+int(double((z-w)>>1)*sin_phi),
00793                           Axis->Label, Qt::black, 12.0, cos_phi, sin_phi));
00794   }
00795 }
00796 
00797 // --------------------------------------------------------------
00798 int Rect3DDiagram::calcDiagram()
00799 {
00800   Lines.clear();
00801   Texts.clear();
00802   Arcs.clear();
00803 
00804   double GridStep, corr, zD, zDstep, GridNum;
00805   // get size of text using the screen-compatible metric
00806   QFontMetrics metrics(QucsSettings.font, 0);
00807 
00808   x3 = x2 + 7;
00809   int z, z2, o, w;
00810 
00811   char *zBuffer=0;   // hidden line algorithm
00812   tBound *Bounds=0;
00813 
00814 
00815   // =====  give "step" the right sign  ==================================
00816   xAxis.step = fabs(xAxis.step);
00817   if(xAxis.limit_min > xAxis.limit_max)
00818     xAxis.step *= -1.0;
00819 
00820   yAxis.step = fabs(yAxis.step);
00821   if(yAxis.limit_min > yAxis.limit_max)
00822     yAxis.step *= -1.0;
00823 
00824   zAxis.step = fabs(zAxis.step);
00825   if(zAxis.limit_min > zAxis.limit_max)
00826     zAxis.step *= -1.0;
00827 
00828 
00829   // =====  calculate Axis.up and Axis.low  ==============================
00830   if(xAxis.log) {
00831     if(xAxis.autoScale) {
00832       if(xAxis.max*xAxis.min <= 0.0)  goto Frame;  // invalid
00833     }
00834     else  if(xAxis.limit_min*xAxis.limit_max <= 0.0)  goto Frame;  // invalid
00835     calcAxisLogScale(&xAxis, z, zD, zDstep, corr, x2);
00836   }
00837   else  calcAxisScale(&xAxis, GridNum, zD, zDstep, GridStep, double(x2));
00838     
00839   if(yAxis.log) {
00840     if(yAxis.autoScale) {
00841       if(yAxis.max*yAxis.min <= 0.0)  goto Frame;  // invalid
00842     }
00843     else  if(yAxis.limit_min*yAxis.limit_max <= 0.0)  goto Frame;  // invalid
00844     calcAxisLogScale(&yAxis, z, zD, zDstep, corr, x2);
00845   }
00846   else  calcAxisScale(&yAxis, GridNum, zD, zDstep, GridStep, double(x2));
00847 
00848   if(zAxis.log) {
00849     if(zAxis.autoScale) {
00850       if(zAxis.max*zAxis.min <= 0.0)  goto Frame;  // invalid
00851     }
00852     else  if(zAxis.limit_min*zAxis.limit_max <= 0.0)  goto Frame;  // invalid
00853     calcAxisLogScale(&zAxis, z, zD, zDstep, corr, x2);
00854   }
00855   else  calcAxisScale(&zAxis, GridNum, zD, zDstep, GridStep, double(x2));
00856 
00857 
00858   // ===  calculate transformation coefficients from rotation angles ===
00859   calcCoefficients();
00860 
00861   // =====  check calculate position of axes in 2D rectangle ===========
00862   int X[8], Y[8];
00863   o = calcCross(X, Y);
00864   // "o" is now the index of the origin coordinates.
00865 
00866 
00867   // =====  paint coordinate cross  ====================================
00868   // xy area
00869   Lines.append(new Line(X[o^1], Y[o^1], X[o^3], Y[o^3], QPen(Qt::black,0)));
00870   Lines.append(new Line(X[o^2], Y[o^2], X[o^3], Y[o^3], QPen(Qt::black,0)));
00871 
00872   // yz area
00873   Lines.append(new Line(X[o^2], Y[o^2], X[o^6], Y[o^6], QPen(Qt::black,0)));
00874   Lines.append(new Line(X[o^4], Y[o^4], X[o^6], Y[o^6], QPen(Qt::black,0)));
00875 
00876   // xz area
00877   Lines.append(new Line(X[o^1], Y[o^1], X[o^5], Y[o^5], QPen(Qt::black,0)));
00878   Lines.append(new Line(X[o^4], Y[o^4], X[o^5], Y[o^5], QPen(Qt::black,0)));
00879 
00880 
00881   // =====  create axis  =============================================
00882   if(X[o^1] < X[o^2])  w = 2;   // use which z axis ?
00883   else  w = 1;
00884 
00885   z = o^2;
00886   if(z & 1)  z ^= 1;  // from where to where ?
00887   z2 = z^1;
00888   createAxis(&xAxis, w == 2, X[z], Y[z], X[z2], Y[z2]);
00889 
00890   z = o^1;
00891   if(z & 2)  z ^= 2;  // from where to where ?
00892   z2 = z^2;
00893   createAxis(&yAxis, w == 1, X[z], Y[z], X[z2], Y[z2]);
00894 
00895   z = o^w;
00896   if(z & 4)  z ^= 4;  // from where to where ?
00897   z2 = z^4;
00898   createAxis(&zAxis, true, X[z], Y[z], X[z2], Y[z2]);
00899 
00900 
00901   if(hideLines) {
00902     w = (x2+1) * (y2/8 + 1);
00903     // To store the pixel coordinates that are already used (hidden).
00904     // Use one bit per pixel.
00905     zBuffer = (char*)malloc(w);
00906     memset(zBuffer, 0, w);
00907 
00908     // To store the boundings of the current polygon.
00909     Bounds = (tBound*)malloc((x2+1) * sizeof(tBound));
00910   }
00911 
00912   // hide invisible parts of graphs
00913   removeHiddenLines(zBuffer, Bounds);
00914 
00915   if(hideLines) {
00916     // now hide invisible part of coordinate cross
00917     tPoint3D *MemTmp = Mem;
00918     Mem = (tPoint3D*)malloc( 10*sizeof(tPoint3D) );
00919     
00920     removeHiddenCross(X[o^1], Y[o^1], X[o], Y[o], zBuffer, Bounds); // x axis
00921     removeHiddenCross(X[o^2], Y[o^2], X[o], Y[o], zBuffer, Bounds); // y axis
00922     removeHiddenCross(X[o^4], Y[o^4], X[o], Y[o], zBuffer, Bounds); // z axis
00923   
00924     free(Mem);
00925     Mem = MemTmp;  // write back values
00926 
00927     free(Bounds);
00928     free(zBuffer);
00929   }
00930   else {
00931     Lines.append(new Line(X[o], Y[o], X[o^1], Y[o^1], QPen(Qt::black,0)));
00932     Lines.append(new Line(X[o], Y[o], X[o^2], Y[o^2], QPen(Qt::black,0)));
00933     Lines.append(new Line(X[o], Y[o], X[o^4], Y[o^4], QPen(Qt::black,0)));
00934   }
00935 
00936   pMem = Mem;
00937   return 3;
00938 
00939 
00940 Frame:   // jump here if error occurred (e.g. impossible log boundings)
00941   Lines.append(new Line(0,  y2, x2, y2, QPen(Qt::black,0)));
00942   Lines.append(new Line(x2, y2, x2,  0, QPen(Qt::black,0)));
00943   Lines.append(new Line(0,   0, x2,  0, QPen(Qt::black,0)));
00944   Lines.append(new Line(0,  y2,  0,  0, QPen(Qt::black,0)));
00945   return 0;
00946 }
00947 
00948 // ------------------------------------------------------------
00949 // g->Points must already be empty!!!
00950 void Rect3DDiagram::calcData(Graph *g)
00951 {
00952   if(!pMem)  return;
00953   if(!g->cPointsY) return;
00954 
00955   int Size = ((2*(g->axis(0)->count) + 1) * g->countY) + 10;
00956   Size *= 2;  // memory for cross grid lines
00957 
00958   g->resizeScrPoints(Size);
00959   auto p = g->begin();
00960   auto p_end = g->begin();
00961   p_end += Size - 9;   // limit of buffer
00962 
00963 
00964   p->setStrokeEnd();
00965   ++p;
00966   switch(g->Style) {
00967     case GRAPHSTYLE_SOLID:
00968     case GRAPHSTYLE_DASH:
00969     case GRAPHSTYLE_DOT:
00970     case GRAPHSTYLE_LONGDASH:
00971       do {
00972 
00973         while(1) {
00974           if(pMem->done & 11)    // is grid point ?
00975             if(pMem->done & 4) { // is hidden
00976               if(pMem > Mem) {
00977                 if((pMem-1)->done & 12)
00978                   break;
00979               }
00980               else  break;
00981       }
00982           FIT_MEMORY_SIZE;  // need to enlarge memory block ?
00983     (p++)->setScr(pMem->x, pMem->y);
00984           break;
00985         }
00986 
00987         FIT_MEMORY_SIZE;  // need to enlarge memory block ?
00988         if(pMem->done & 8)  (p++)->setBranchEnd();
00989 
00990         if(pMem->done & 4)   // point invisible ?
00991           if((p-1)->isPt())
00992             (p++)->setStrokeEnd();
00993 
00994       } while(((pMem++)->done & 256) == 0);
00995       p->setGraphEnd();
00996       break;
00997 
00998     default:  // symbol (e.g. star) at each point **********************
00999       do {
01000         while(1) {
01001           if(pMem->done & 11)    // is grid point ?
01002             if(pMem->done & 4) { // is hidden
01003               if(pMem > Mem) {
01004                 if((pMem-1)->done & 12)
01005                   break;
01006               }
01007               else  break;
01008       }
01009 
01010           (p++)->setScr(pMem->x, pMem->y);
01011           break;
01012         }
01013 
01014         if(pMem->done & 8)
01015           (p++)->setBranchEnd();
01016       } while(((pMem++)->done & 512) == 0);
01017       p->setGraphEnd();
01018   }
01019 
01020   return;
01021 }
01022 
01023 // ------------------------------------------------------------
01024 // The labels are created during "calcDiagram", but the memory
01025 // for the coordinates is released here.
01026 void Rect3DDiagram::createAxisLabels()
01027 {
01028   if(Mem)  free (Mem);
01029   Mem  = 0;
01030   pMem = 0;
01031 }
01032 
01033 // ------------------------------------------------------------
01034 bool Rect3DDiagram::insideDiagram(float x, float y) const
01035 {
01036   return (regionCode(x, y) == 0);
01037 }
01038 
01039 // ------------------------------------------------------------
01040 Diagram* Rect3DDiagram::newOne()
01041 {
01042   return new Rect3DDiagram();
01043 }
01044 
01045 // ------------------------------------------------------------
01046 Element* Rect3DDiagram::info(QString& Name, char* &BitmapFile, bool getNewOne)
01047 {
01048   Name = QObject::tr("3D-Cartesian");
01049   BitmapFile = (char *) "rect3d";
01050 
01051   if(getNewOne)  return new Rect3DDiagram();
01052   return 0;
01053 }
01054 
01055 // vim:ts=8:sw=2:noet
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines