My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
marker.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  marker.cpp - description
3  -------------------
4  begin : Sat Apr 10 2004
5  copyright : (C) 2003 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 #include "marker.h"
19 #include "diagram.h"
20 #include "graph.h"
21 #include "main.h"
22 
23 #include <qstring.h>
24 #include <qwidget.h>
25 #include <qpainter.h>
26 
27 #include <limits.h>
28 #include <math.h>
29 #include <stdlib.h>
30 
31 
32 Marker::Marker(Diagram *Diag_, Graph *pg_, int _nn, int cx_, int cy_)
33 {
34  Type = isMarker;
35  isSelected = transparent = false;
36 
37  Diag = Diag_;
38  pGraph = pg_;
39  Precision = 3; // before createText()
40  VarPos = 0;
41  numMode = nVarPos = 0;
42  cx = cx_;
43  cy = -cy_;
44  fCX = float(cx);
45  fCY = float(cy);
46 
47  if(!pGraph) makeInvalid();
48  else initText(_nn); // finally create marker
49 
50  x1 = cx + 60;
51  y1 = -cy - 60;
52 }
53 
55 {
56  if(VarPos) free(VarPos);
57 }
58 
59 // ---------------------------------------------------------------------
61 {
62  if(pGraph->cPointsX.isEmpty()) {
63  makeInvalid();
64  return;
65  }
66 
67  Axis *pa;
68  if(pGraph->yAxisNo == 0) pa = &(Diag->yAxis);
69  else pa = &(Diag->zAxis);
70  double Dummy = 0.0; // needed for 2D graph in 3D diagram
71  double *px, *py=&Dummy, *pz;
72  Text = "";
73 
74  bool isCross = false;
75  int nn, nnn, m, x, y, d, dmin = INT_MAX;
76  DataX *pD = pGraph->cPointsX.first();
77  px = pD->Points;
78  nnn = pD->count;
79  DataX *pDy = pGraph->cPointsX.next();
80  if(pDy) { // only for 3D diagram
81  nn = pGraph->countY * pD->count;
82  py = pDy->Points;
83  if(n >= nn) { // is on cross grid ?
84  isCross = true;
85  n -= nn;
86  n /= nnn;
87  px += (n % nnn);
88  if(pGraph->cPointsX.next()) // more than 2 indep variables ?
89  n = (n % nnn) + (n / nnn) * nnn * pDy->count;
90  nnn = pDy->count;
91  }
92  else py += (n/pD->count) % pDy->count;
93  }
94 
95  // find exact marker position
96  m = nnn - 1;
97  pz = pGraph->cPointsY + 2*n;
98  for(nn=0; nn<nnn; nn++) {
99  Diag->calcCoordinate(px, pz, py, &fCX, &fCY, pa);
100  if(isCross) {
101  px--;
102  py++;
103  pz += 2*(pD->count-1);
104  }
105  x = int(fCX+0.5) - cx;
106  y = int(fCY+0.5) - cy;
107  d = x*x + y*y;
108  if(d < dmin) {
109  dmin = d;
110  m = nn;
111  }
112  }
113  if(isCross) m *= pD->count;
114  n += m;
115 
116  nVarPos = 0;
117  nn = (pGraph->cPointsX.count() + 2) * sizeof(double);
118  if(VarPos)
119  VarPos = (double*)realloc(VarPos, nn);
120  else
121  VarPos = (double*)malloc(nn);
122 
123  // gather text of all independent variables
124  nn = n;
125  for(pD = pGraph->cPointsX.first(); pD!=0; pD = pGraph->cPointsX.next()) {
126  px = pD->Points + (nn % pD->count);
127  VarPos[nVarPos++] = *px;
128  Text += pD->Var + ": " + QString::number(*px,'g',Precision) + "\n";
129  nn /= pD->count;
130  }
131 
132  // gather text of dependent variable
133  pz = pGraph->cPointsY + 2*n;
134  Text += pGraph->Var + ": ";
135  switch(numMode) {
136  case 0: Text += complexRect(*pz, *(pz+1), Precision);
137  break;
138  case 1: Text += complexDeg(*pz, *(pz+1), Precision);
139  break;
140  case 2: Text += complexRad(*pz, *(pz+1), Precision);
141  break;
142  }
143  VarPos[nVarPos] = *pz;
144  VarPos[nVarPos+1] = *(pz+1);
145 
146  px = VarPos;
147  if(py != &Dummy) // 2D in 3D diagram ?
148  py = VarPos + 1;
149  Diag->calcCoordinate(px, pz, py, &fCX, &fCY, pa);
150 
151  if(!Diag->insideDiagram(fCX, fCY)) {
152  // if marker out of valid bounds, point to origin
153  if((Diag->Name.left(4) != "Rect") && (Diag->Name != "Curve")) {
154  fCX = float(Diag->x2 >> 1);
155  fCY = float(Diag->y2 >> 1);
156  }
157  else
158  fCX = fCY = 0.0;
159  }
160 
161  cx = int(fCX+0.5);
162  cy = int(fCY+0.5);
164 }
165 
166 // ---------------------------------------------------------------------
168 {
169  if(!(pGraph->cPointsY)) {
170  makeInvalid();
171  return;
172  }
173 
174  VarPos = (double*)realloc(VarPos,
175  (pGraph->cPointsX.count() + 2) * sizeof(double));
176 
177  while((unsigned int)nVarPos < pGraph->cPointsX.count())
178  VarPos[nVarPos++] = 0.0; // fill up VarPos
179 
180 
181  // independent variables
182  Text = "";
183  double *pp, v;
184  int n = 0, m = 1, i;
185  DataX *pD;
186  nVarPos = 0;
187  for(pD = pGraph->cPointsX.first(); pD!=0; pD = pGraph->cPointsX.next()) {
188  pp = pD->Points;
189  v = VarPos[nVarPos];
190  for(i=pD->count; i>1; i--) { // find appropiate marker position
191  if(fabs(v-(*pp)) < fabs(v-(*(pp+1)))) break;
192  pp++;
193  n += m;
194  }
195 
196  m *= pD->count;
197  VarPos[nVarPos++] = *pp;
198  Text += pD->Var + ": " + QString::number(*pp,'g',Precision) + "\n";
199  }
200 
201 
202  v = 0.0; // needed for 2D graph in 3D diagram
203  double *py=&v, *pz = pGraph->cPointsY + 2*n;
204  pD = pGraph->cPointsX.first();
205  if(pGraph->cPointsX.next()) {
206  py = pGraph->cPointsX.current()->Points; // only for 3D diagram
207  py += (n / pD->count) % pGraph->cPointsX.current()->count;
208  }
209 
210  Text += pGraph->Var + ": ";
211  switch(numMode) {
212  case 0: Text += complexRect(*pz, *(pz+1), Precision);
213  break;
214  case 1: Text += complexDeg(*pz, *(pz+1), Precision);
215  break;
216  case 2: Text += complexRad(*pz, *(pz+1), Precision);
217  break;
218  }
219  VarPos[nVarPos] = *pz;
220  VarPos[nVarPos+1] = *(pz+1);
221 
222  Axis *pa;
223  if(pGraph->yAxisNo == 0) pa = &(Diag->yAxis);
224  else pa = &(Diag->zAxis);
225  pp = &(VarPos[0]);
226 
227  Diag->calcCoordinate(pp, pz, py, &fCX, &fCY, pa);
228 
229  if(!Diag->insideDiagram(fCX, fCY)) {
230  // if marker out of valid bounds, point to origin
231  if((Diag->Name.left(4) != "Rect") && (Diag->Name != "Curve")) {
232  fCX = float(Diag->x2 >> 1);
233  fCY = float(Diag->y2 >> 1);
234  }
235  else
236  fCX = fCY = 0.0;
237  }
238 
239  cx = int(fCX+0.5);
240  cy = int(fCY+0.5);
242 }
243 
244 // ---------------------------------------------------------------------
246 {
247  cx = 0;
248  cy = 0;
249  if(Diag) if(Diag->Name.left(4) != "Rect") if(Diag->Name != "Curve") {
250  cx = Diag->x2 >> 1;
251  cy = Diag->y2 >> 1;
252  }
253  Text = QObject::tr("invalid");
254 
255  fCX = float(cx);
256  fCY = float(cy);
258 }
259 
260 // ---------------------------------------------------------------------
261 void Marker::getTextSize(const QFont& Font)
262 {
263  QFontMetrics metrics(Font);
264  QSize r = metrics.size(0, Text);
265  x2 = r.width()+5;
266  y2 = r.height()+5;
267 }
268 
269 // ---------------------------------------------------------------------
270 bool Marker::moveLeftRight(bool left)
271 {
272  int n;
273  double *px;
274 
275  DataX *pD = pGraph->cPointsX.getFirst();
276  px = pD->Points;
277  if(!px) return false;
278  for(n=0; n<pD->count; n++) {
279  if(VarPos[0] <= *px) break;
280  px++;
281  }
282  if(n == pD->count) px--;
283 
284  if(left) {
285  if(px <= pD->Points) return false;
286  px--; // one position to the left
287  }
288  else {
289  if(px >= (pD->Points + pD->count - 1)) return false;
290  px++; // one position to the right
291  }
292  VarPos[0] = *px;
293  createText();
294 
295  return true;
296 }
297 
298 // ---------------------------------------------------------------------
299 bool Marker::moveUpDown(bool up)
300 {
301  int n, i=0;
302  double *px;
303 
304  DataX *pD = pGraph->cPointsX.first();
305  if(!pD) return false;
306 
307  if(up) { // move upwards ? **********************
308  do {
309  i++;
310  pD = pGraph->cPointsX.next();
311  if(!pD) return false;
312  px = pD->Points;
313  if(!px) return false;
314  for(n=1; n<pD->count; n++) { // go through all data points
315  if(fabs(VarPos[i]-(*px)) < fabs(VarPos[i]-(*(px+1)))) break;
316  px++;
317  }
318 
319  } while(px >= (pD->Points + pD->count - 1)); // go to next dimension ?
320 
321  px++; // one position up
322  VarPos[i] = *px;
323  while(i > 1) {
324  pD = pGraph->cPointsX.prev();
325  i--;
326  VarPos[i] = *(pD->Points);
327  }
328  }
329  else { // move downwards **********************
330  do {
331  i++;
332  pD = pGraph->cPointsX.next();
333  if(!pD) return false;
334  px = pD->Points;
335  if(!px) return false;
336  for(n=0; n<pD->count; n++) {
337  if(fabs(VarPos[i]-(*px)) < fabs(VarPos[i]-(*(px+1)))) break;
338  px++;
339  }
340 
341  } while(px <= pD->Points); // go to next dimension ?
342 
343  px--; // one position down
344  VarPos[i] = *px;
345  while(i > 1) {
346  pD = pGraph->cPointsX.prev();
347  i--;
348  VarPos[i] = *(pD->Points + pD->count - 1);
349  }
350  }
351  createText();
352 
353  return true;
354 }
355 
356 // ---------------------------------------------------------------------
357 void Marker::paint(ViewPainter *p, int x0, int y0)
358 {
359  // Workaround for bug in Qt: If WorldMatrix is turned off, \n in the
360  // text creates a terrible mess.
361  p->Painter->setWorldXForm(true);
362  QWMatrix wm = p->Painter->worldMatrix();
363  p->Painter->setWorldMatrix(QWMatrix());
364 
365  int x2_, y2_;
366  p->Painter->setPen(QPen(QPen::black,1));
367  x2_ = p->drawText(Text, x0+x1+3, y0+y1+3, &y2_);
368  x2_ += int(6.0*p->Scale);
369  y2_ += int(6.0*p->Scale);
370  if(!transparent) {
371  p->eraseRect(x0+x1, y0+y1, x2_, y2_);
372  p->drawText(Text, x0+x1+3, y0+y1+3);
373  }
374  p->Painter->setWorldMatrix(wm);
375  p->Painter->setWorldXForm(false);
376 
377  p->Painter->setPen(QPen(QPen::darkMagenta,0));
378  p->drawRectD(x0+x1, y0+y1, x2_, y2_);
379 
380  x2 = int(float(x2_) / p->Scale);
381  y2 = int(float(y2_) / p->Scale);
382 
383  int x1_, y1_;
384  p->map(x0+x1, y0+y1, x1_, y1_);
385  // which corner of rectangle should be connected to line ?
386  if(cx < x1+(x2>>1)) {
387  if(-cy >= y1+(y2>>1))
388  y1_ += y2_ - 1;
389  }
390  else {
391  x1_ += x2_ - 1;
392  if(-cy >= y1+(y2>>1))
393  y1_ += y2_ - 1;
394  }
395  float fx2, fy2;
396  fx2 = (float(x0)+fCX)*p->Scale + p->DX;
397  fy2 = (float(y0)-fCY)*p->Scale + p->DY;
398  p->Painter->drawLine(x1_, y1_, TO_INT(fx2), TO_INT(fy2));
399 
400  if(isSelected) {
401  p->Painter->setPen(QPen(QPen::darkGray,3));
402  p->drawRoundRect(x0+x1-3, y0+y1-3, x2+6, y2+6);
403  }
404 }
405 
406 // ---------------------------------------------------------------------
407 void Marker::paintScheme(QPainter *p)
408 {
409  int x0 = Diag->cx;
410  int y0 = Diag->cy;
411  p->drawRect(x0+x1, y0+y1, x2, y2);
412 
413  // which corner of rectangle should be connected to line ?
414  if(cx < x1+(x2>>1)) {
415  if(-cy < y1+(y2>>1))
416  p->drawLine(x0+cx, y0-cy, x0+x1, y0+y1);
417  else
418  p->drawLine(x0+cx, y0-cy, x0+x1, y0+y1+y2-1);
419  }
420  else {
421  if(-cy < y1+(y2>>1))
422  p->drawLine(x0+cx, y0-cy, x0+x1+x2-1, y0+y1);
423  else
424  p->drawLine(x0+cx, y0-cy, x0+x1+x2-1, y0+y1+y2-1);
425  }
426 }
427 
428 // ------------------------------------------------------------
429 void Marker::setCenter(int x, int y, bool relative)
430 {
431  if(relative) {
432  x1 += x; y1 += y;
433  }
434  else {
435  x1 = x; y1 = y;
436  }
437 }
438 
439 // -------------------------------------------------------
440 void Marker::Bounding(int& _x1, int& _y1, int& _x2, int& _y2)
441 {
442  if(Diag) {
443  _x1 = Diag->cx + x1;
444  _y1 = Diag->cy + y1;
445  _x2 = Diag->cx + x1+x2;
446  _y2 = Diag->cy + y1+y2;
447  }
448  else {
449  _x1 = x1;
450  _y1 = y1+y2;
451  _x2 = x1+x2;
452  _y2 = y1;
453  }
454 }
455 
456 // ---------------------------------------------------------------------
457 QString Marker::save()
458 {
459  QString s = "<Mkr ";
460 
461  for(int i=0; i<nVarPos; i++)
462  s += QString::number(VarPos[i])+"/";
463  s.at(s.length()-1) = ' ';
464 
465  s += QString::number(x1) +" "+ QString::number(y1) +" "
466  +QString::number(Precision) +" "+ QString::number(numMode);
467  if(transparent) s += " 1>";
468  else s += " 0>";
469 
470  return s;
471 }
472 
473 // ---------------------------------------------------------------------
474 // All graphs must have been loaded before this function !
475 bool Marker::load(const QString& _s)
476 {
477  bool ok;
478  QString s = _s;
479 
480  if(s.at(0) != '<') return false;
481  if(s.at(s.length()-1) != '>') return false;
482  s = s.mid(1, s.length()-2); // cut off start and end character
483 
484  if(s.section(' ',0,0) != "Mkr") return false;
485 
486  int i=0, j;
487  QString n = s.section(' ',1,1); // VarPos
488 
489  nVarPos = 0;
490  j = (n.contains('/') + 3) * sizeof(double);
491  if(VarPos)
492  VarPos = (double*)realloc(VarPos, j);
493  else
494  VarPos = (double*)malloc(j);
495 
496  do {
497  j = n.find('/', i);
498  VarPos[nVarPos++] = n.mid(i,j-i).toDouble(&ok);
499  if(!ok) return false;
500  i = j+1;
501  } while(j >= 0);
502 
503  n = s.section(' ',2,2); // x1
504  x1 = n.toInt(&ok);
505  if(!ok) return false;
506 
507  n = s.section(' ',3,3); // y1
508  y1 = n.toInt(&ok);
509  if(!ok) return false;
510 
511  n = s.section(' ',4,4); // Precision
512  Precision = n.toInt(&ok);
513  if(!ok) return false;
514 
515  n = s.section(' ',5,5); // numMode
516  numMode = n.toInt(&ok);
517  if(!ok) return false;
518 
519  n = s.section(' ',6,6); // transparent
520  if(n.isEmpty()) return true; // is optional
521  if(n == "0") transparent = false;
522  else transparent = true;
523 
524  return true;
525 }
526 
527 // ------------------------------------------------------------------------
528 // Checks if the coordinates x/y point to the marker text. x/y are relative
529 // to diagram cx/cy.
530 bool Marker::getSelected(int x_, int y_)
531 {
532  if(x_ >= x1) if(x_ <= x1+x2) if(y_ >= y1) if(y_ <= y1+y2)
533  return true;
534 
535  return false;
536 }
537 
538 // ------------------------------------------------------------------------
540 {
541  Marker *pm = new Marker(Diag, pGraph_, 0, cx ,cy);
542 
543  pm->x1 = x1; pm->y1 = y1;
544  pm->x2 = x2; pm->y2 = y2;
545 
546  int z = (nVarPos+2) * sizeof(double);
547  if(pm->VarPos)
548  pm->VarPos = (double*)realloc(pm->VarPos, z);
549  else
550  pm->VarPos = (double*)malloc(z);
551 
552  pm->nVarPos = nVarPos;
553  for(z=0; z<nVarPos; z++)
554  pm->VarPos[z] = VarPos[z];
555 
556  pm->Text = Text;
557  pm->transparent = transparent;
558  pm->Precision = Precision;
559  pm->numMode = numMode;
560 
561  return pm;
562 }