My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
rect3ddiagram.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  rect3ddiagram.cpp
3  -------------------
4  begin : Sat Mar 5 2005
5  copyright : (C) 2005 by Michael Margraf
6  email : michael.margraf@alumni.tu-berlin.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <stdlib.h>
23 #include <math.h>
24 #include <float.h>
25 #ifdef __MINGW32__
26 # define finite(x) _finite(x)
27 #endif
28 #include <limits.h>
29 #if HAVE_IEEEFP_H
30 # include <ieeefp.h>
31 #endif
32 
33 #include "rect3ddiagram.h"
34 #include "main.h"
35 
36 Rect3DDiagram::Rect3DDiagram(int _cx, int _cy) : Diagram(_cx, _cy)
37 {
38  x1 = 10; // position of label text
39  y1 = y3 = 7;
40  x2 = 200; // initial size of diagram
41  y2 = 200;
42  x3 = 207; // with some distance for right axes text
43 
44  Mem = pMem = 0; // auxiliary buffer for hidden lines
45 
46  Name = "Rect3D";
47  // symbolic diagram painting
48  Lines.append(new Line(0, 0, cx, 0, QPen(QPen::black,0)));
49  Lines.append(new Line(0, 0, 0, cy, QPen(QPen::black,0)));
50  Lines.append(new Line(0, 0, cx/2, cy/2, QPen(QPen::black,0)));
51 }
52 
54 {
55 }
56 
57 // ------------------------------------------------------------
58 // Calculates the coefficients for 3D -> 2D transformation
59 void Rect3DDiagram::calcCoefficients()
60 {
61  double rX = double(rotX) * M_PI/180.0;
62  double rY = double(rotY) * M_PI/180.0;
63  double rZ = double(rotZ) * M_PI/180.0;
64 
65  cxy = sin(rZ); cxx = cos(rZ);
66  cxz = sin(rY); rY = cos(rY);
67  cyz = sin(rX); czz = cos(rX);
68  rX = cyz*cxz; rZ = czz*cxz;
69  cyx = czz * cxy + rX * cxx;
70  cyy = czz * cxx - rX * cxy;
71  czx = cyz * cxy - rZ * cxx;
72  czy = cyz * cxx + rZ * cxy;
73  cxx *= rY; cxy *= -rY; cyz *= -rY; czz *= rY;
74 }
75 
76 // ------------------------------------------------------------
77 double Rect3DDiagram::calcX_2D(double x, double y, double z)
78 {
79  return (cxx * x + cxy * y + cxz * z) * scaleX;
80 }
81 
82 // ------------------------------------------------------------
83 double Rect3DDiagram::calcY_2D(double x, double y, double z)
84 {
85  return (cyx * x + cyy * y + cyz * z) * scaleY;
86 }
87 
88 // ------------------------------------------------------------
89 double Rect3DDiagram::calcZ_2D(double x, double y, double z)
90 {
91  return czx * x + czy * y + czz * z;
92 }
93 
94 // ------------------------------------------------------------
95 // Determines the position of the coordinate cross, i.e. calculates
96 // "scaleX", "scaleY", "xorig" and "yorig". Returns the corner with
97 // the largest distance from the viewer.
98 int Rect3DDiagram::calcCross(int *Xses, int *Yses)
99 {
100  double x3D, y3D, z3D, x2D[8], y2D[8], z2D;
101  double XMIN_2D, XMAX_2D, YMIN_2D, YMAX_2D, ZMIN_2D;
102 
103  int z, Center = 0; // used to save minimum z (is center for axis cross)
104  scaleX = scaleY = 1.0; // used in "calcX_2D" and "calcY_2D"
105  XMIN_2D = YMIN_2D = XMAX_2D = YMAX_2D = ZMIN_2D = 0.0; // origin is zero
106  for(z=0; z<8; z++) { // check 2D coordinates of all 8 quadrat corners
107  if(z & 1) x3D = 1.0; else x3D = 0.0;
108  if(z & 2) y3D = 1.0; else y3D = 0.0;
109  if(z & 4) z3D = 1.0; else z3D = 0.0;
110  x2D[z] = calcX_2D(x3D, y3D, z3D);
111  y2D[z] = calcY_2D(x3D, y3D, z3D);
112  z2D = calcZ_2D(x3D, y3D, z3D);
113 
114  if(x2D[z] < XMIN_2D) XMIN_2D = x2D[z];
115  if(x2D[z] > XMAX_2D) XMAX_2D = x2D[z];
116  if(y2D[z] < YMIN_2D) YMIN_2D = y2D[z];
117  if(y2D[z] > YMAX_2D) YMAX_2D = y2D[z];
118  if(z2D < ZMIN_2D) { ZMIN_2D = z2D; Center = z; }
119  }
120 
121  scaleX = double(x2) / (XMAX_2D - XMIN_2D); // scaling 3D -> 2D transformation
122  scaleY = double(y2) / (YMAX_2D - YMIN_2D);
123  xorig = -XMIN_2D * scaleX; // position of origin
124  yorig = -YMIN_2D * scaleY;
125 
126  for(z=0; z<8; z++) { // calculate 2D coordinates of all corners
127  *(Xses+z) = int(x2D[z] * scaleX + 0.5 + xorig);
128  *(Yses+z) = int(y2D[z] * scaleY + 0.5 + yorig);
129  }
130  return Center;
131 }
132 
133 // ------------------------------------------------------------
134 // Is needed for markers.
135 void Rect3DDiagram::calcCoordinate(double* &xD, double* &zD, double* &yD,
136  float *px, float *py, Axis*)
137 {
138  double x3D = *(zD++);
139  double y3D = *(zD++);
140  double z3D;
141  if(zAxis.log) {
142  z3D = sqrt(x3D*x3D + y3D*y3D);
143 /* if(z3D <= 0.0) clipping not yet correct implemented
144  z3D = -1e5; // "negative infinity"
145  else*/
146  z3D = log10(z3D / fabs(zAxis.low)) / log10(zAxis.up / zAxis.low);
147  }
148  else {
149  if(fabs(y3D) > 1e-250) // preserve negative values if no complex number
150  x3D = sqrt(x3D*x3D + y3D*y3D);
151  z3D = (x3D - zAxis.low) / (zAxis.up - zAxis.low);
152  }
153 
154  x3D = *(xD++);
155  if(xAxis.log) {
156  x3D /= xAxis.low;
157 /* if(x3D <= 0.0) clipping not yet correct implemented
158  x3D = -1e5; // "negative infinity"
159  else*/
160  x3D = log10(x3D) / log10(xAxis.up / xAxis.low);
161  }
162  else
163  x3D = (x3D - xAxis.low) / (xAxis.up - xAxis.low);
164 
165  if(yAxis.log) {
166  y3D = (*yD) / yAxis.low;
167 /* if(y3D <= 0.0) clipping not yet correct implemented
168  y3D = -1e5; // "negative infinity"
169  else*/
170  y3D = log10(y3D) / log10(yAxis.up / yAxis.low);
171  }
172  else
173  y3D = (*yD - yAxis.low) / (yAxis.up - yAxis.low);
174 
175  *px = float(calcX_2D(x3D, y3D, z3D)) + xorig;
176  *py = float(calcY_2D(x3D, y3D, z3D)) + yorig;
177 
178  if(finite(*px))
179  if(finite(*py))
180  return;
181 
182  *px = float(xorig);
183  *py = float(yorig);
184 }
185 
186 // ------------------------------------------------------------
187 void Rect3DDiagram::calcCoordinate3D(double x, double y, double zr, double zi,
188  tPoint3D *p, tPointZ *pz)
189 {
190  if(zAxis.log) {
191  zr = sqrt(zr*zr + zi*zi);
192 /* if(zr <= 0.0) clipping not yet correct implemented
193  zr = -1e5; // "negative infinity"
194  else*/
195  zr = log10(zr / fabs(zAxis.low)) / log10(zAxis.up / zAxis.low);
196  }
197  else {
198  if(fabs(zi) > 1e-250) // preserve negative values if no complex number
199  zr = sqrt(zr*zr + zi*zi);
200  zr = (zr - zAxis.low) / (zAxis.up - zAxis.low);
201  }
202 
203  if(xAxis.log) {
204  x /= xAxis.low;
205 /* if(x <= 0.0) clipping not yet correct implemented
206  x = -1e5; // "negative infinity"
207  else*/
208  x = log10(x) / log10(xAxis.up / xAxis.low);
209  }
210  else
211  x = (x - xAxis.low) / (xAxis.up - xAxis.low);
212 
213  if(yAxis.log) {
214  y = y / yAxis.low;
215 /* if(y <= 0.0) clipping not yet correct implemented
216  y = -1e5; // "negative infinity"
217  else*/
218  y = log10(y) / log10(yAxis.up / yAxis.low);
219  }
220  else
221  y = (y - yAxis.low) / (yAxis.up - yAxis.low);
222 
223  p->x = int(calcX_2D(x, y, zr) + 0.5 + xorig);
224  p->y = int(calcY_2D(x, y, zr) + 0.5 + yorig);
225  p->No = pz->No = p-Mem;
226  p->done = 0;
227  pz->z = float(calcZ_2D(x, y, zr));
228 }
229 
230 // --------------------------------------------------------------
231 bool Rect3DDiagram::isHidden(int x, int y, tBound *Bounds, char *zBuffer)
232 {
233  // remember the boundings of the polygon
234  if( (Bounds+x)->max < y ) (Bounds+x)->max = y;
235  if( (Bounds+x)->min > y ) (Bounds+x)->min = y;
236 
237  // diagram area already used ?
238  return ( *(zBuffer + (y>>3) + x * ((y2+7)>>3)) & (1 << (y & 7)) ) != 0;
239 }
240 
241 // --------------------------------------------------------------
242 // Enlarge memory block if neccessary.
243 void Rect3DDiagram::enlargeMemoryBlock(tPoint3D* &MemEnd)
244 {
245  if(pMem >= MemEnd) {
246  int Size = MemEnd - Mem + 256;
247  MemEnd = Mem;
248  Mem = (tPoint3D*)realloc(Mem, Size*sizeof(tPoint3D));
249  pMem += Mem - MemEnd;
250  MemEnd = Mem + Size - 5;
251  }
252 }
253 
254 // --------------------------------------------------------------
255 // Calculate all 2D points of the line between point "p" and "p+1".
256 // Parameters: p - pointer on 3D coordinate of line start point
257 // (p+1 points onto line end point)
258 // MemEnd - pointer where memory block ends
259 // Bounds - memory block for occupied polygon area
260 // zBuffer - memory block for occupied diagram area
261 void Rect3DDiagram::calcLine(tPoint3D* &p, tPoint3D* &MemEnd,
262  tBound *Bounds, char *zBuffer)
263 {
264  int Pos_;
265  int x1_ = p->x, y1_ = p->y;
266  int x2_ = (p+1)->x, y2_ = (p+1)->y;
267 
268  bool wasHidden = isHidden(x1_, y1_, Bounds, zBuffer);
269  if(wasHidden)
270  if((p->done & 1) == 0)
271  p->done |= 4; // mark as hidden
272 
273  int ax_ = 0, ay_ = 0;
274  int ix_, iy_, dx_, dy_, of_;
275 
276  if(x2_ >= x1_) {
277  dx_ = x2_ - x1_;
278  ix_ = 1;
279  }
280  else {
281  dx_ = x1_ - x2_;
282  ix_ = -1;
283  }
284 
285  if(y2_ >= y1_) {
286  dy_ = y2_ - y1_;
287  iy_ = 1;
288  }
289  else {
290  dy_ = y1_ - y2_;
291  iy_ = -1;
292  }
293 
294  if(dx_ < dy_) {
295  of_ = dx_; // exchange dx and dy
296  dx_ = dy_;
297  dy_ = of_;
298 
299  ax_ = iy_;
300  ay_ = ix_;
301  ix_ = iy_ = 0;
302  }
303 
304  of_ = dx_ >> 1;
305  for(int i=dx_; i>1; i--) { // calculate each point of the line
306  x1_ += ix_;
307  y1_ += ax_;
308  of_ += dy_;
309  if(of_ > dx_) {
310  of_ -= dx_;
311  x1_ += ay_;
312  y1_ += iy_;
313  }
314 
315  if( isHidden(x1_, y1_, Bounds, zBuffer) != wasHidden )
316  if((p->done & 1) == 0) {
317  wasHidden = !wasHidden;
318  pMem->x = x1_;
319  pMem->y = y1_;
320  pMem->No = p->No;
321  pMem->done = 0;
322  if(wasHidden) pMem->done = 4; // mark as hidden
323  pMem++;
324 
325  Pos_ = p - Mem;
326  // Enlarge memory block if neccessary.
327  enlargeMemoryBlock(MemEnd); // this may make "p" invalid (realloc)
328  p = Mem + Pos_; // rebuild "p"
329  }
330  }
331 
332  // extra treatment for last point (create no further point)
333  if(isHidden((p+1)->x, (p+1)->y, Bounds, zBuffer))
334  if(((p+1)->done & 1) == 0)
335  (p+1)->done |= 4; // mark as hidden
336 
337  p->done |= 1; // mark as already worked on
338 }
339 
340 // --------------------------------------------------------------
341 // Compare functions for GNU qsort routine.
342 int Rect3DDiagram::comparePoint3D(const void *Point1, const void *Point2)
343 {
344  return ((tPoint3D*)Point1)->No - ((tPoint3D*)Point2)->No;
345 }
346 int Rect3DDiagram::comparePointZ(const void *Point1, const void *Point2)
347 {
348  if((((tPointZ*)Point2)->z - ((tPointZ*)Point1)->z) < 0.0f)
349  return -1;
350  return 1;
351 }
352 
353 // --------------------------------------------------------------
354 // Removes the invisible parts of the graph.
355 void Rect3DDiagram::removeHiddenLines(char *zBuffer, tBound *Bounds)
356 {
357  double Dummy = 0.0; // number for 1-dimensional data in 3D cartesian
358  double *px, *py, *pz;
359 
360  Graph *g;
361  tPoint3D *p;
362  int i, j, z, dx, dy, Size=0;
363  // pre-calculate buffer size to avoid reallocations in the first step
364  for(g = Graphs.first(); g!=0; g = Graphs.next())
365  if(g->cPointsY)
366  Size += g->cPointsX.getFirst()->count * g->countY;
367 
368  // "Mem" should be the last malloc to simplify realloc
369  tPointZ *zMem = (tPointZ*)malloc( (Size+2)*sizeof(tPointZ) );
370  Mem = (tPoint3D*)malloc( 2*(Size+2)*sizeof(tPoint3D) );
371 
372  pMem = Mem;
373  tPointZ *zp = zMem, *zp_tmp;
374 
375  // ...............................................................
376  for(g = Graphs.first(); g!=0; g = Graphs.next()) {
377 
378  pz = g->cPointsY;
379  if(!pz) continue;
380  if(g->cPointsX.count() < 1) continue;
381 
382  py = &Dummy;
383  if(g->countY > 1) py = g->cPointsX.at(1)->Points;
384 
385  p = pMem; // save status for cross grid
386  zp_tmp = zp;
387  // ..........................................
388  // calculate coordinates of all lines
389  dx = g->cPointsX.first()->count;
390  if(g->countY > 1) dy = g->cPointsX.next()->count;
391  else dy = 0;
392  for(i=g->countY-1; i>=0; i--) { // y coordinates
393  px = g->cPointsX.getFirst()->Points;
394 
395  for(j=dx; j>0; j--) { // x coordinates
396  calcCoordinate3D(*(px++), *py, *pz, *(pz+1), pMem++, zp++);
397  pz += 2;
398  }
399 
400  (pMem-1)->done |= 8; // mark as "last in line"
401  py++;
402  if(dy > 0) if((i % dy) == 0)
403  py = g->cPointsX.at(1)->Points;
404  }
405  (pMem-1)->done |= 512; // mark as "last point before grid"
406 
407  // ..........................................
408  // copy points for cross lines ("dx", "dy" still unchanged ! )
409  if(g->countY > 1) {
410  zp = zp_tmp;
411  for(j=g->countY/dy; j>0; j--) { // every plane
412  for(i=dx; i>0; i--) { // every branch
413  for(z=dy; z>0; z--) { // every point
414  pMem->x = p->x;
415  pMem->y = p->y;
416  pMem->No = pMem-Mem;
417  pMem->done = 0;
418  zp->NoCross = pMem-Mem; // position of its cross grid
419  pMem++;
420  p += dx; // next coordinate
421  zp += dx;
422  }
423  (pMem-1)->done |= 8; // mark as "last in line"
424  p -= dx*dy - 1; // next z coordinate
425  zp -= dx*dy - 1;
426  }
427  p += dx*(dy-1);
428  zp += dx*(dy-1);
429  }
430  }
431  (pMem-1)->done |= 256; // mark as "very last point"
432 
433 
434  if(hideLines) {
435  // ..........................................
436  // Calculate the z-coordinate of all polygons by building the
437  // sum of the z-coordinates of all of its 4 corners.
438  // After this, each point represents one polygon. The unneccessary
439  // points are filled with "-FLTMAX".
440  zp = zp_tmp;
441  // "dx" and "dy" are still unchanged !
442  for(i=g->countY-1; i>=0; i--) { // all branches
443  if(dy > 0) if(i % dy) {
444  for(j=dx-1; j>0; j--) { // x coordinates
445  zp->z += (zp+1)->z + (zp+dx)->z + (zp+dx+1)->z;
446  zp++;
447  }
448  zp->z = -FLT_MAX; // last one not needed
449  zp++;
450  continue;
451  }
452 
453  // last line not needed
454  for(j=dx; j>0; j--) { // x coordinates
455  zp->z = -FLT_MAX; // last one not needed
456  zp++;
457  }
458  }
459  } // of "if(hideLines)"
460 
461  } // of "for(Graphs)"
462 
463 
464  if(!hideLines) { // do not hide invisible lines
465  free(zMem);
466  return;
467  }
468 
469 #if 0
470  qDebug("##########################################");
471  qDebug("Size 1b: Size=%d, %d, %d", Size, pMem-Mem, zp-zMem);
472  for(tPoint3D *p=Mem; p<pMem; p++)
473  qDebug("xyPoints: %d/%d - %d - %d", p->x, p->y, p->No, p->done);
474  qDebug("------------------------------------------");
475  for(tPointZ *p=zMem; p-zMem<Size; p++)
476  qDebug("zPoints: %g - %d", p->z, p->No);
477 #endif
478 
479 
480  // ..........................................
481  // Sort z-coordinates (greatest first).
482  // After this the polygons that have the smallest distance to the
483  // viewer are on top of the list and thus, will be processed first.
484  qsort(zMem, Size, sizeof(tPointZ), comparePointZ);
485 
486 #if 0
487  qDebug("--------------------------- z sorting");
488  for(tPointZ *p=zMem; p-zMem<Size; p++)
489  qDebug("zPoints: %g - %d", p->z, p->No);
490 #endif
491 
492 
493  // ..........................................
494  char *pc;
495  tPoint3D *MemEnd = Mem + 2*Size - 5; // limit of buffer
496 
497  zp = zMem;
498  for(g = Graphs.first(); g!=0; g = Graphs.next()) {
499  if(!g->cPointsY) continue;
500  dx = g->cPointsX.first()->count;
501  if(g->countY > 1) dy = g->cPointsX.next()->count;
502  else dy = 1;
503 
504  // look for hidden lines ...
505  for(int No = g->countY/dy * (dx-1)*(dy-1); No>0; No--) {
506 
507  // reset the polygon bounding buffer
508  for(i=x2; i>=0; i--) {
509  (Bounds+i)->max = INT_MIN;
510  (Bounds+i)->min = INT_MAX;
511  }
512 
513  // work on all 4 lines of polygon
514  p = Mem + zp->No; // polygon corner coordinates
515  calcLine(p, MemEnd, Bounds, zBuffer);
516 
517  p += dx;
518  calcLine(p, MemEnd, Bounds, zBuffer);
519 
520  p = Mem + zp->NoCross; // cross grid
521  calcLine(p, MemEnd, Bounds, zBuffer);
522 
523  p += dy;
524  calcLine(p, MemEnd, Bounds, zBuffer);
525 
526  // mark the area of the polygon (stored in "*Bounds") as used
527  for(i=x2-1; i>=0; i--) // all x coordinates
528  if( (Bounds+i)->max > INT_MIN) {
529  pc = zBuffer + i * ((y2+7)>>3);
530  for(j=(Bounds+i)->min; j<=(Bounds+i)->max; j++) // all y coordinates
531  *(pc + (j>>3)) |= (1 << (j & 7));
532  }
533 
534  zp++; // next polygon
535  }
536 
537  } // of "for(Graphs)"
538 
539 #if 0
540  qDebug("--------------------------- hidden lines %d", pMem-Mem);
541  for(tPoint3D *p=Mem; p<pMem; p++)
542  qDebug("xyPoints: %d/%d - %d - %d", p->x, p->y, p->No, p->done);
543 #endif
544 
545  free(zMem);
546 
547  // sort "No" (least one first)
548  qsort(Mem, pMem - Mem, sizeof(tPoint3D), comparePoint3D);
549 
550 #if 0
551  qDebug("--------------------------- last sorting %d", pMem-Mem);
552  for(tPoint3D *p=Mem; p<pMem; p++)
553  qDebug("xyPoints: %d/%d - %d - %d", p->x, p->y, p->No, p->done);
554  qDebug("\n");
555 #endif
556 }
557 
558 // --------------------------------------------------------------
559 // Removes the invisible parts of the coordinate cross.
560 void Rect3DDiagram::removeHiddenCross(int x1_, int y1_, int x2_, int y2_,
561  char *zBuffer, tBound *Bounds)
562 {
563  pMem = Mem;
564 
565  pMem->x = x1_;
566  pMem->y = y1_;
567  pMem->No = 0;
568  pMem->done = 0;
569  pMem++;
570 
571  pMem->x = x2_;
572  pMem->y = y2_;
573  pMem->No = 1;
574  pMem->done = 0;
575  pMem++;
576 
577  tPoint3D *p = Mem+6;
578  calcLine(Mem, p, Bounds, zBuffer);
579  *pMem = *(Mem+1);
580  *(Mem+1) = *Mem;
581  p = Mem+2;
582  do {
583  if(((p-1)->done & 4) == 0)
584  Lines.append(new Line((p-1)->x, (p-1)->y, p->x, p->y, QPen(QPen::black,0)));
585  p++;
586  } while(p <= pMem);
587 }
588 
589 // --------------------------------------------------------------
591 {
592  int i;
593  double a, b, c;
594 
595  if(xAxis.autoScale) {// check before, to preserve limit exchange (max < min)
596  if(xAxis.log) {
597  calcAxisLogScale(&xAxis, i, a, b, c, x2);
598  xAxis.step = 1.0;
599  }
600  else calcAxisScale(&xAxis, a, b, c, xAxis.step, double(x2));
603  }
604 
605  if(yAxis.autoScale) {// check before, to preserve limit exchange (max < min)
606  if(yAxis.log) {
607  calcAxisLogScale(&yAxis, i, a, b, c, y2);
608  yAxis.step = 1.0;
609  }
610  else calcAxisScale(&yAxis, a, b, c, yAxis.step, double(y2));
613  }
614 
615  if(zAxis.autoScale) {// check before, to preserve limit exchange (max < min)
616  if(zAxis.log) {
617  calcAxisLogScale(&zAxis, i, a, b, c, y2);
618  zAxis.step = 1.0;
619  }
620  else calcAxisScale(&zAxis, a, b, c, zAxis.step, double(y2));
623  }
624 }
625 
626 // --------------------------------------------------------------
627 int Rect3DDiagram::calcAxis(Axis *Axis, int x, int y,
628  double xD, double phi, bool Right)
629 {
630  double GridStep, corr, yD, stepD, GridNum, Expo;
631  double xstepD, ystepD;
632 
633  QString tmp;
634  QFontMetrics metrics(QucsSettings.font);
635  int maxWidth = 0;
636  int count, gx, gy, w;
637 
638  if(phi > 0.0) Expo = phi - M_PI/2.0;
639  else Expo = phi + M_PI/2.0;
640  gx = int(5.4 * cos(Expo) + 0.5); // short grid marker lines
641  gy = int(5.4 * sin(Expo) + 0.5);
642 
643 
644 if(Axis->log) {
645 
646  bool back = calcAxisLogScale(Axis, w, yD, stepD, corr, int(xD));
647 
648  double upD = Axis->up;
649  if(yD > 1.5*stepD) yD = 10.0*stepD; // always start at power of 10
650  if(back) {
651  upD = Axis->low;
652  phi += M_PI;
653  xD = 0.0;
654  }
655 
656  int xLen, yLen;
657  ystepD = corr * log10(yD / fabs(Axis->low));
658  while(ystepD <= xD) { // create all grid lines
659 
660  tmp = StringNiceNum(yD);
661  if(Axis->up < 0.0) tmp = '-'+tmp;
662  w = metrics.width(tmp); // width of text
663  if(maxWidth < w) maxWidth = w;
664 
665  xLen = int(ystepD * cos(phi) + 0.5) + x;
666  yLen = int(ystepD * sin(phi) + 0.5) + y;
667  if(Right)
668  Texts.append(new Text(xLen+3+gx, yLen-6+gy, tmp));
669  else
670  Texts.append(new Text(xLen-w-2-gx, yLen-6-gy, tmp));
671 
672  // short grid marks
673  Lines.append(new Line(xLen-gx, yLen-gy, xLen+gx, yLen+gy,
674  QPen(QPen::black,0)));
675  yD *= 10.0;
676  ystepD += corr;
677  }
678 
679 }
680 else { // not logarithmical
681  calcAxisScale(Axis, GridNum, yD, stepD, GridStep, xD);
682  count = int((xD - yD) / stepD) + 1; // number of grids
683 
684  xstepD = stepD * cos(phi);
685  ystepD = stepD * sin(phi);
686  xD = yD * cos(phi) + 0.5 + double(x);
687  yD = yD * sin(phi) + 0.5 + double(y);
688 
689  if(Axis->up == 0.0) Expo = log10(fabs(Axis->up-Axis->low));
690  else Expo = log10(fabs(Axis->up));
691 
692  for(; count>0; count--) {
693  x = int(xD);
694  y = int(yD);
695  if(fabs(GridNum) < 0.01*pow(10.0, Expo)) GridNum = 0.0; // make 0 really 0
696  tmp = StringNiceNum(GridNum);
697 
698  w = metrics.width(tmp); // width of text
699  if(maxWidth < w) maxWidth = w;
700  if(Right)
701  Texts.append(new Text(x+3+gx, y-6+gy, tmp)); // place text right
702  else
703  Texts.append(new Text(x-w-2-gx, y-6-gy, tmp)); // place left
704  GridNum += GridStep;
705 
706  // short grid marks
707  Lines.append(new Line(x-gx, y-gy, x+gx, y+gy, QPen(QPen::black,0)));
708  xD += xstepD;
709  yD += ystepD;
710  }
711 } // of "if(ylog) ... else ..."
712 
713  return maxWidth+5;
714 }
715 
716 // --------------------------------------------------------------
717 void Rect3DDiagram::createAxis(Axis *Axis, bool Right,
718  int x1_, int y1_, int x2_, int y2_)
719 {
720  DataX *pD;
721  Graph *pg;
722  double phi, cos_phi, sin_phi;
723  int x, y, z, w, valid, Index = 0;
724  if(Axis == &yAxis) Index = 1;
725 
726  QString s;
727  QFontMetrics metrics(QucsSettings.font);
728 
729  x = x2_ - x1_;
730  y = y2_ - y1_;
731  cos_phi = sqrt(double(x*x) + double(y*y));
732  phi = atan2(double(y), double(x));
733 
734  valid = calcAxis(Axis, x1_, y1_, cos_phi, phi, Right); // axis numbering
735  z = (int)cos_phi;
736  cos_phi = cos(phi);
737  sin_phi = sin(phi);
738 
739  if(fabs(phi-1e-5) > M_PI/2.0) {
740  x1_ = x2_; cos_phi *= -1;
741  y1_ = y2_; sin_phi *= -1;
742  }
743  x = x1_ + int(double(valid)*sin_phi);
744  y = y1_ - int(double(valid)*cos_phi);
745  if(Axis->Label.isEmpty()) {
746  // write all labels ----------------------------------------
747  for(pg = Graphs.first(); pg != 0; pg = Graphs.next()) {
748  if(Axis != &zAxis) {
749  if(!pg->cPointsY) continue;
750  if(valid < 0) {
751  delete[] pg->cPointsY;
752  pg->cPointsY = 0;
753  continue;
754  }
755  pD = pg->cPointsX.at(Index);
756  if(!pD) continue;
757  s = pD->Var;
758  }
759  else {
760  s = pg->Var;
761  if(!pg->cPointsY) s += INVALID_STR;
762  }
763  x += int(double(metrics.lineSpacing())*sin_phi);
764  y -= int(double(metrics.lineSpacing())*cos_phi);
765  w = metrics.width(s);
766  Texts.append(new Text(x+int(double((z-w)>>1)*cos_phi),
767  y+int(double((z-w)>>1)*sin_phi),
768  s, pg->Color, 12.0, cos_phi, sin_phi));
769  }
770  }
771  else {
772  x += int(double(metrics.lineSpacing())*sin_phi);
773  y -= int(double(metrics.lineSpacing())*cos_phi);
774  w = metrics.width(Axis->Label);
775  Texts.append(new Text(x+int(double((z-w)>>1)*cos_phi),
776  y+int(double((z-w)>>1)*sin_phi),
777  Axis->Label, Qt::black, 12.0, cos_phi, sin_phi));
778  }
779 }
780 
781 // --------------------------------------------------------------
783 {
784  Lines.clear();
785  Texts.clear();
786  Arcs.clear();
787 
788  double GridStep, corr, zD, zDstep, GridNum;
789  QFontMetrics metrics(QucsSettings.font);
790 
791  x3 = x2 + 7;
792  int z, z2, o, w;
793 
794  char *zBuffer=0; // hidden line algorithm
795  tBound *Bounds=0;
796 
797 
798  // ===== give "step" the right sign ==================================
799  xAxis.step = fabs(xAxis.step);
801  xAxis.step *= -1.0;
802 
803  yAxis.step = fabs(yAxis.step);
805  yAxis.step *= -1.0;
806 
807  zAxis.step = fabs(zAxis.step);
809  zAxis.step *= -1.0;
810 
811 
812  // ===== calculate Axis.up and Axis.low ==============================
813  if(xAxis.log) {
814  if(xAxis.autoScale) {
815  if(xAxis.max*xAxis.min <= 0.0) goto Frame; // invalid
816  }
817  else if(xAxis.limit_min*xAxis.limit_max <= 0.0) goto Frame; // invalid
818  calcAxisLogScale(&xAxis, z, zD, zDstep, corr, x2);
819  }
820  else calcAxisScale(&xAxis, GridNum, zD, zDstep, GridStep, double(x2));
821 
822  if(yAxis.log) {
823  if(yAxis.autoScale) {
824  if(yAxis.max*yAxis.min <= 0.0) goto Frame; // invalid
825  }
826  else if(yAxis.limit_min*yAxis.limit_max <= 0.0) goto Frame; // invalid
827  calcAxisLogScale(&yAxis, z, zD, zDstep, corr, x2);
828  }
829  else calcAxisScale(&yAxis, GridNum, zD, zDstep, GridStep, double(x2));
830 
831  if(zAxis.log) {
832  if(zAxis.autoScale) {
833  if(zAxis.max*zAxis.min <= 0.0) goto Frame; // invalid
834  }
835  else if(zAxis.limit_min*zAxis.limit_max <= 0.0) goto Frame; // invalid
836  calcAxisLogScale(&zAxis, z, zD, zDstep, corr, x2);
837  }
838  else calcAxisScale(&zAxis, GridNum, zD, zDstep, GridStep, double(x2));
839 
840 
841  // === calculate transformation coefficients from rotation angles ===
842  calcCoefficients();
843 
844  // ===== check calculate position of axes in 2D rectangle ===========
845  int X[8], Y[8];
846  o = calcCross(X, Y);
847  // "o" is now the index of the origin coordinates.
848 
849 
850  // ===== paint coordinate cross ====================================
851  // xy area
852  Lines.append(new Line(X[o^1], Y[o^1], X[o^3], Y[o^3], QPen(QPen::black,0)));
853  Lines.append(new Line(X[o^2], Y[o^2], X[o^3], Y[o^3], QPen(QPen::black,0)));
854 
855  // yz area
856  Lines.append(new Line(X[o^2], Y[o^2], X[o^6], Y[o^6], QPen(QPen::black,0)));
857  Lines.append(new Line(X[o^4], Y[o^4], X[o^6], Y[o^6], QPen(QPen::black,0)));
858 
859  // xz area
860  Lines.append(new Line(X[o^1], Y[o^1], X[o^5], Y[o^5], QPen(QPen::black,0)));
861  Lines.append(new Line(X[o^4], Y[o^4], X[o^5], Y[o^5], QPen(QPen::black,0)));
862 
863 
864  // ===== create axis =============================================
865  if(X[o^1] < X[o^2]) w = 2; // use which z axis ?
866  else w = 1;
867 
868  z = o^2;
869  if(z & 1) z ^= 1; // from where to where ?
870  z2 = z^1;
871  createAxis(&xAxis, w == 2, X[z], Y[z], X[z2], Y[z2]);
872 
873  z = o^1;
874  if(z & 2) z ^= 2; // from where to where ?
875  z2 = z^2;
876  createAxis(&yAxis, w == 1, X[z], Y[z], X[z2], Y[z2]);
877 
878  z = o^w;
879  if(z & 4) z ^= 4; // from where to where ?
880  z2 = z^4;
881  createAxis(&zAxis, true, X[z], Y[z], X[z2], Y[z2]);
882 
883 
884  if(hideLines) {
885  w = (x2+1) * (y2/8 + 1);
886  // To store the pixel coordinates that are already used (hidden).
887  // Use one bit per pixel.
888  zBuffer = (char*)malloc(w);
889  memset(zBuffer, 0, w);
890 
891  // To store the boundings of the current polygon.
892  Bounds = (tBound*)malloc((x2+1) * sizeof(tBound));
893  }
894 
895  // hide invisible parts of graphs
896  removeHiddenLines(zBuffer, Bounds);
897 
898  if(hideLines) {
899  // now hide invisible part of coordinate cross
900  tPoint3D *MemTmp = Mem;
901  Mem = (tPoint3D*)malloc( 10*sizeof(tPoint3D) );
902 
903  removeHiddenCross(X[o^1], Y[o^1], X[o], Y[o], zBuffer, Bounds); // x axis
904  removeHiddenCross(X[o^2], Y[o^2], X[o], Y[o], zBuffer, Bounds); // y axis
905  removeHiddenCross(X[o^4], Y[o^4], X[o], Y[o], zBuffer, Bounds); // z axis
906 
907  free(Mem);
908  Mem = MemTmp; // write back values
909 
910  free(Bounds);
911  free(zBuffer);
912  }
913  else {
914  Lines.append(new Line(X[o], Y[o], X[o^1], Y[o^1], QPen(QPen::black,0)));
915  Lines.append(new Line(X[o], Y[o], X[o^2], Y[o^2], QPen(QPen::black,0)));
916  Lines.append(new Line(X[o], Y[o], X[o^4], Y[o^4], QPen(QPen::black,0)));
917  }
918 
919  pMem = Mem;
920  return 3;
921 
922 
923 Frame: // jump here if error occurred (e.g. impossible log boundings)
924  Lines.append(new Line(0, y2, x2, y2, QPen(QPen::black,0)));
925  Lines.append(new Line(x2, y2, x2, 0, QPen(QPen::black,0)));
926  Lines.append(new Line(0, 0, x2, 0, QPen(QPen::black,0)));
927  Lines.append(new Line(0, y2, 0, 0, QPen(QPen::black,0)));
928  return 0;
929 }
930 
931 // ------------------------------------------------------------
932 // g->Points must already be empty!!!
934 {
935  if(!pMem) return;
936  if(!g->cPointsY) return;
937 
938  int tmp;
939  int Size = ((2*(g->cPointsX.getFirst()->count) + 1) * g->countY) + 10;
940  Size *= 2; // memory for cross grid lines
941 
942  double *py;
943  if(g->countY > 1) py = g->cPointsX.at(1)->Points;
944 
945  float *p = (float*)malloc( Size*sizeof(float) ); // create memory for points
946  float *p_end;
947  g->ScrPoints = p_end = p;
948  p_end += Size - 9; // limit of buffer
949 
950 
951  *(p++) = STROKEEND;
952  float dx=0.0, dy=0.0, xtmp=0.0, ytmp=0.0;
953  double Stroke=10.0, Space=10.0; // length of strokes and spaces in pixel
954  switch(g->Style) {
955  case 0: // ***** solid line **********************************
956  do {
957 
958  while(1) {
959  if(pMem->done & 11) // is grid point ?
960  if(pMem->done & 4) { // is hidden
961  if(pMem > Mem) {
962  if((pMem-1)->done & 12)
963  break;
964  }
965  else break;
966  }
967 
968  FIT_MEMORY_SIZE; // need to enlarge memory block ?
969  *(p++) = pMem->x;
970  *(p++) = pMem->y;
971  break;
972  }
973 
974  FIT_MEMORY_SIZE; // need to enlarge memory block ?
975  if(pMem->done & 8) *(p++) = BRANCHEND; // new branch
976 
977  if(pMem->done & 4) // point invisible ?
978  if( *(p-1) >= 0 ) // line already interrupted ?
979  *(p++) = STROKEEND;
980 
981  } while(((pMem++)->done & 256) == 0);
982  *p = GRAPHEND;
983  return;
984 
985  case GRAPHSTYLE_DASH:
986  Stroke = 10.0; Space = 6.0;
987  break;
988  case GRAPHSTYLE_DOT:
989  Stroke = 2.0; Space = 4.0;
990  break;
991  case GRAPHSTYLE_LONGDASH:
992  Stroke = 24.0; Space = 8.0;
993  break;
994 
995  default: // symbol (e.g. star) at each point **********************
996  do {
997  while(1) {
998  if(pMem->done & 11) // is grid point ?
999  if(pMem->done & 4) { // is hidden
1000  if(pMem > Mem) {
1001  if((pMem-1)->done & 12)
1002  break;
1003  }
1004  else break;
1005  }
1006 
1007  *(p++) = pMem->x;
1008  *(p++) = pMem->y;
1009  break;
1010  }
1011 
1012  if(pMem->done & 8)
1013  *(p++) = BRANCHEND; // new branch
1014  } while(((pMem++)->done & 512) == 0);
1015  *p = GRAPHEND;
1016  return;
1017  }
1018 
1019 
1020  // ********** dashed, dotted, ... *******************************
1021  int Counter = 0; // counter for number of points in queue
1022  double alpha, dist = -Stroke;
1023  int Flag = 1; // current state: 1=stroke, 0=space
1024  do {
1025  while(1) {
1026  if(pMem->done & 11) // is grid point ?
1027  if(pMem->done & 4) { // is hidden
1028  if(pMem > Mem) {
1029  if((pMem-1)->done & 12)
1030  break;
1031  }
1032  else break;
1033  }
1034 
1035  xtmp = pMem->x;
1036  ytmp = pMem->y;
1037  Counter++;
1038  break;
1039  }
1040 
1041 
1042  if(Counter > 1) {
1043  if(Counter == 2) {
1044  *(p++) = dx; // if first points of branch -> paint first one
1045  *(p++) = dy;
1046  }
1047  dx = xtmp - dx;
1048  dy = ytmp - dy;
1049  dist += sqrt(double(dx*dx + dy*dy)); // distance between points
1050  if((Flag == 1) && (dist <= 0.0)) {
1051  FIT_MEMORY_SIZE; // need to enlarge memory block ?
1052  *(p++) = xtmp; // if stroke then save points
1053  *(p++) = ytmp;
1054  }
1055  else {
1056  alpha = atan2(double(dy), double(dx)); // slope for interpolation
1057  while(dist > 0) { // stroke or space finished ?
1058  FIT_MEMORY_SIZE; // need to enlarge memory block ?
1059 
1060  *(p++) = xtmp - float(dist*cos(alpha)); // linearly interpolate
1061  *(p++) = ytmp - float(dist*sin(alpha));
1062 
1063  if(Flag == 0) {
1064  dist -= Stroke;
1065  if(dist <= 0) {
1066  *(p++) = xtmp; // don't forget point after ...
1067  *(p++) = ytmp; // ... interpolated point
1068  }
1069  }
1070  else {
1071  dist -= Space;
1072  if(*(p-3) < 0) p -= 2;
1073  else *(p++) = STROKEEND;
1074  }
1075  Flag ^= 1; // toggle between stroke and space
1076  }
1077  }
1078 
1079  } // of "if(Counter > 1)"
1080 
1081 
1082  dx = xtmp;
1083  dy = ytmp;
1084 
1085  if(pMem->done & 8) {
1086  if(*(p-3) == STROKEEND)
1087  p -= 3; // no single point after "no stroke"
1088  else if(*(p-3) == BRANCHEND) {
1089  if((*(p-2) < 0) || (*(p-1) < 0))
1090  p -= 2; // erase last hidden point
1091  }
1092  *(p++) = BRANCHEND; // new branch
1093  Counter = 0;
1094  Flag = 1;
1095  dist = -Stroke;
1096  }
1097 
1098  if(pMem->done & 4) // point invisible ?
1099  if( *(p-1) >= 0 ) // line already interrupted ?
1100  *(p++) = STROKEEND;
1101 
1102  } while(((pMem++)->done & 256) == 0);
1103 
1104  *p = GRAPHEND;
1105 
1106 
1107 /*
1108 int z = p-g->Points+1;
1109 p = g->Points;
1110 qDebug("\n****** p=%p", p);
1111 for(int zz=0; zz<z; zz+=2)
1112  qDebug("c: %d/%d", *(p+zz), *(p+zz+1));
1113 qDebug("ENDE");*/
1114 }
1115 
1116 // ------------------------------------------------------------
1117 // The labels are created during "calcDiagram", but the memory
1118 // for the coordinates is released here.
1120 {
1121  if(Mem) free (Mem);
1122  Mem = 0;
1123  pMem = 0;
1124 }
1125 
1126 // ------------------------------------------------------------
1127 bool Rect3DDiagram::insideDiagram(float x, float y)
1128 {
1129  return (regionCode(x, y) == 0);
1130 }
1131 
1132 // ------------------------------------------------------------
1134 {
1135  return new Rect3DDiagram();
1136 }
1137 
1138 // ------------------------------------------------------------
1139 Element* Rect3DDiagram::info(QString& Name, char* &BitmapFile, bool getNewOne)
1140 {
1141  Name = QObject::tr("3D-Cartesian");
1142  BitmapFile = (char *) "rect3d";
1143 
1144  if(getNewOne) return new Rect3DDiagram();
1145  return 0;
1146 }