Qucs-GUI
0.0.19
|
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