Qucs-GUI
0.0.19
|
00001 /*************************************************************************** 00002 marker.cpp - description 00003 ------------------- 00004 begin : Sat Apr 10 2004 00005 copyright : (C) 2003 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 00024 #include "marker.h" 00025 #include "diagram.h" 00026 #include "graph.h" 00027 #include "main.h" 00028 00029 #include <QString> 00030 #include <QPainter> 00031 #include <QDebug> 00032 00033 #include <limits.h> 00034 #include <cmath> 00035 #include <stdlib.h> 00036 00037 #include "misc.h" 00038 00039 static double default_Z0=50; 00040 00049 Marker::Marker(Graph *pg_, int branchNo, int cx_, int cy_) : 00050 Element(), 00051 pGraph(pg_), 00052 Precision(3), 00053 numMode(0), 00054 Z0(default_Z0) // BUG: see declaration. 00055 { 00056 Type = isMarker; 00057 isSelected = transparent = false; 00058 00059 cx = cx_; 00060 cy = -cy_; 00061 fCX = float(cx); 00062 fCY = float(cy); 00063 if(!pGraph){ 00064 makeInvalid(); 00065 }else{ 00066 initText(branchNo); // finally create marker 00067 createText(); 00068 } 00069 00070 x1 = cx + 60; 00071 y1 = -cy - 60; 00072 00073 } 00074 00075 Marker::~Marker() 00076 { 00077 } 00078 00079 // --------------------------------------------------------------------- 00086 void Marker::initText(int n) 00087 { 00088 if(pGraph->isEmpty()) { 00089 makeInvalid(); 00090 return; 00091 } 00092 00093 Axis const *pa; 00094 assert(diag()); 00095 if(pGraph->yAxisNo == 0) pa = &(diag()->yAxis); 00096 else pa = &(diag()->zAxis); 00097 double Dummy = 0.0; // needed for 2D graph in 3D diagram 00098 double *px, *py=&Dummy, *pz; 00099 Text = ""; 00100 00101 bool isCross = false; 00102 int nn, nnn, m, x, y, d, dmin = INT_MAX; 00103 DataX const *pD = pGraph->axis(0); 00104 px = pD->Points; 00105 nnn = pD->count; 00106 DataX const *pDy = pGraph->axis(1); 00107 if(pDy) { // only for 3D diagram 00108 nn = pGraph->countY * pD->count; 00109 py = pDy->Points; 00110 if(n >= nn) { // is on cross grid ? 00111 isCross = true; 00112 n -= nn; 00113 n /= nnn; 00114 px += (n % nnn); 00115 if(pGraph->axis(2)) // more than 2 indep variables ? 00116 n = (n % nnn) + (n / nnn) * nnn * pDy->count; 00117 nnn = pDy->count; 00118 } 00119 else py += (n/pD->count) % pDy->count; 00120 } 00121 00122 // find exact marker position 00123 m = nnn - 1; 00124 pz = pGraph->cPointsY + 2*n; 00125 for(nn=0; nn<nnn; nn++) { 00126 diag()->calcCoordinate(px, pz, py, &fCX, &fCY, pa); 00127 ++px; 00128 pz += 2; 00129 if(isCross) { 00130 px--; 00131 py++; 00132 pz += 2*(pD->count-1); 00133 } 00134 x = int(fCX+0.5) - cx; 00135 y = int(fCY+0.5) - cy; 00136 d = x*x + y*y; 00137 if(d < dmin) { 00138 dmin = d; 00139 m = nn; 00140 } 00141 } 00142 if(isCross) m *= pD->count; 00143 n += m; 00144 00145 // why check over and over again?! do in the right place and just assert otherwise. 00146 if(VarPos.size() != pGraph->numAxes()){ 00147 qDebug() << "huh, wrong size" << VarPos.size() << pGraph->numAxes(); 00148 VarPos.resize(pGraph->numAxes()); 00149 } 00150 00151 // gather text of all independent variables 00152 nn = n; 00153 for(unsigned i=0; (pD = pGraph->axis(i)); ++i) { 00154 px = pD->Points + (nn % pD->count); 00155 VarPos[i] = *px; 00156 Text += pD->Var + ": " + QString::number(*px,'g',Precision) + "\n"; 00157 nn /= pD->count; 00158 } 00159 00160 // createText(); 00161 } 00162 00163 // --------------------------------------------------------------------- 00169 void Marker::createText() 00170 { 00171 if(!(pGraph->cPointsY)) { 00172 makeInvalid(); 00173 return; 00174 } 00175 00176 unsigned nVarPos = VarPos.size(); 00177 00178 if(nVarPos > pGraph->numAxes()){ 00179 qDebug() << "huh, VarPos too big?!"; 00180 } 00181 if(nVarPos != pGraph->numAxes()){ 00182 qDebug() << "padding" << VarPos.size() << pGraph->numAxes(); 00183 VarPos.resize(pGraph->numAxes()); 00184 while((unsigned int)nVarPos < pGraph->numAxes()){ 00185 VarPos[nVarPos++] = 0.; // pad 00186 } 00187 } 00188 00189 // independent variables 00190 Text = ""; 00191 double *pp; 00192 nVarPos = pGraph->numAxes(); 00193 DataX const *pD; 00194 00195 auto p = pGraph->findSample(VarPos); 00196 VarDep[0] = p.first; 00197 VarDep[1] = p.second; 00198 00199 double v=0.; // needed for 2D graph in 3D diagram 00200 double *py=&v; 00201 pD = pGraph->axis(0); 00202 if(pGraph->axis(1)) { 00203 *py = VarPos[1]; 00204 }else{ 00205 qDebug() << *py << "is not" << VarPos[1]; // does it really matter?! 00206 } 00207 00208 double pz[2]; 00209 pz[0] = VarDep[0]; 00210 pz[1] = VarDep[1]; 00211 00212 // now actually create text. 00213 for(unsigned ii=0; (pD=pGraph->axis(ii)); ++ii) { 00214 Text += pD->Var + ": " + QString::number(VarPos[ii],'g',Precision) + "\n"; 00215 } 00216 00217 Text += pGraph->Var + ": "; 00218 switch(numMode) { 00219 case nM_Rect: Text += misc::complexRect(*pz, *(pz+1), Precision); 00220 break; 00221 case nM_Deg: Text += misc::complexDeg(*pz, *(pz+1), Precision); 00222 break; 00223 case nM_Rad: Text += misc::complexRad(*pz, *(pz+1), Precision); 00224 break; 00225 } 00226 00227 assert(diag()); 00228 Text += diag()->extraMarkerText(this); 00229 00230 Axis const *pa; 00231 if(pGraph->yAxisNo == 0) pa = &(diag()->yAxis); 00232 else pa = &(diag()->zAxis); 00233 pp = &(VarPos[0]); 00234 00235 diag()->calcCoordinate(pp, pz, py, &fCX, &fCY, pa); 00236 diag()->finishMarkerCoordinates(fCX, fCY); 00237 00238 cx = int(fCX+0.5); 00239 cy = int(fCY+0.5); 00240 getTextSize(); 00241 } 00242 00243 // --------------------------------------------------------------------- 00244 void Marker::makeInvalid() 00245 { 00246 fCX = fCY = -1e3; // invalid coordinates 00247 assert(diag()); 00248 diag()->finishMarkerCoordinates(fCX, fCY); // leave to diagram 00249 cx = int(fCX+0.5); 00250 cy = int(fCY+0.5); 00251 00252 Text = QObject::tr("invalid"); 00253 getTextSize(); 00254 } 00255 00256 // --------------------------------------------------------------------- 00257 void Marker::getTextSize() 00258 { 00259 // get size of text using the screen-compatible metric 00260 QFontMetrics metrics(QucsSettings.font, 0); 00261 QSize r = metrics.size(0, Text); 00262 x2 = r.width()+5; 00263 y2 = r.height()+5; 00264 } 00265 00266 // --------------------------------------------------------------------- 00267 bool Marker::moveLeftRight(bool left) 00268 { 00269 int n; 00270 double *px; 00271 00272 DataX const *pD = pGraph->axis(0); 00273 px = pD->Points; 00274 if(!px) return false; 00275 for(n=0; n<pD->count; n++) { 00276 if(VarPos[0] <= *px) break; 00277 px++; 00278 } 00279 if(n == pD->count) px--; 00280 00281 if(left) { 00282 if(px <= pD->Points) return false; 00283 px--; // one position to the left 00284 } 00285 else { 00286 if(px >= (pD->Points + pD->count - 1)) return false; 00287 px++; // one position to the right 00288 } 00289 VarPos[0] = *px; 00290 createText(); 00291 00292 return true; 00293 } 00294 00295 // --------------------------------------------------------------------- 00296 bool Marker::moveUpDown(bool up) 00297 { 00298 int n, i=0; 00299 double *px; 00300 00301 DataX const *pD = pGraph->axis(0); 00302 if(!pD) return false; 00303 00304 if(up) { // move upwards ? ********************** 00305 do { 00306 pD = pGraph->axis(++i); 00307 if(!pD) return false; 00308 px = pD->Points; 00309 if(!px) return false; 00310 for(n=1; n<pD->count; n++) { // go through all data points 00311 if(fabs(VarPos[i]-(*px)) < fabs(VarPos[i]-(*(px+1)))) break; 00312 px++; 00313 } 00314 00315 } while(px >= (pD->Points + pD->count - 1)); // go to next dimension ? 00316 00317 px++; // one position up 00318 VarPos[i] = *px; 00319 while(i > 1) { 00320 pD = pGraph->axis(--i); 00321 VarPos[i] = *(pD->Points); 00322 } 00323 } 00324 else { // move downwards ********************** 00325 do { 00326 pD = pGraph->axis(++i); 00327 if(!pD) return false; 00328 px = pD->Points; 00329 if(!px) return false; 00330 for(n=0; n<pD->count; n++) { 00331 if(fabs(VarPos[i]-(*px)) < fabs(VarPos[i]-(*(px+1)))) break; 00332 px++; 00333 } 00334 00335 } while(px <= pD->Points); // go to next dimension ? 00336 00337 px--; // one position down 00338 VarPos[i] = *px; 00339 while(i > 1) { 00340 pD = pGraph->axis(--i); 00341 VarPos[i] = *(pD->Points + pD->count - 1); 00342 } 00343 } 00344 createText(); 00345 00346 return true; 00347 } 00348 00349 // --------------------------------------------------------------------- 00350 void Marker::paint(ViewPainter *p, int x0, int y0) 00351 { 00352 // keep track of painter state 00353 p->Painter->save(); 00354 00355 // Workaround for bug in Qt: If WorldMatrix is turned off, \n in the 00356 // text creates a terrible mess. 00357 p->Painter->setWorldMatrixEnabled(true); 00358 QMatrix wm = p->Painter->worldMatrix(); 00359 p->Painter->setWorldMatrix(QMatrix()); 00360 00361 int x2_, y2_; 00362 p->Painter->setPen(QPen(Qt::black,1)); 00363 x2_ = p->drawText(Text, x0+x1+3, y0+y1+3, &y2_); 00364 x2_ += int(6.0*p->Scale); 00365 y2_ += int(6.0*p->Scale); 00366 if(!transparent) { 00367 p->eraseRect(x0+x1, y0+y1, x2_, y2_); 00368 p->drawText(Text, x0+x1+3, y0+y1+3); 00369 } 00370 p->Painter->setWorldMatrix(wm); 00371 p->Painter->setWorldMatrixEnabled(false); 00372 00373 // restore painter state 00374 p->Painter->restore(); 00375 00376 p->Painter->setPen(QPen(Qt::darkMagenta,0)); 00377 p->drawRectD(x0+x1, y0+y1, x2_, y2_); 00378 00379 x2 = int(float(x2_) / p->Scale); 00380 y2 = int(float(y2_) / p->Scale); 00381 00382 int x1_, y1_; 00383 p->map(x0+x1, y0+y1, x1_, y1_); 00384 // which corner of rectangle should be connected to line ? 00385 if(cx < x1+(x2>>1)) { 00386 if(-cy >= y1+(y2>>1)) 00387 y1_ += y2_ - 1; 00388 } 00389 else { 00390 x1_ += x2_ - 1; 00391 if(-cy >= y1+(y2>>1)) 00392 y1_ += y2_ - 1; 00393 } 00394 float fx2, fy2; 00395 fx2 = (float(x0)+fCX)*p->Scale + p->DX; 00396 fy2 = (float(y0)-fCY)*p->Scale + p->DY; 00397 p->Painter->drawLine(x1_, y1_, TO_INT(fx2), TO_INT(fy2)); 00398 00399 if(isSelected) { 00400 p->Painter->setPen(QPen(Qt::darkGray,3)); 00401 p->drawRoundRect(x0+x1-3, y0+y1-3, x2+6, y2+6); 00402 } 00403 } 00404 00405 // --------------------------------------------------------------------- 00406 void Marker::paintScheme(QPainter *p) 00407 { 00408 assert(diag()); 00409 int x0 = diag()->cx; 00410 int y0 = diag()->cy; 00411 p->drawRect(x0+x1, y0+y1, x2, y2); 00412 00413 // which corner of rectangle should be connected to line ? 00414 if(cx < x1+(x2>>1)) { 00415 if(-cy < y1+(y2>>1)) 00416 p->drawLine(x0+cx, y0-cy, x0+x1, y0+y1); 00417 else 00418 p->drawLine(x0+cx, y0-cy, x0+x1, y0+y1+y2-1); 00419 } 00420 else { 00421 if(-cy < y1+(y2>>1)) 00422 p->drawLine(x0+cx, y0-cy, x0+x1+x2-1, y0+y1); 00423 else 00424 p->drawLine(x0+cx, y0-cy, x0+x1+x2-1, y0+y1+y2-1); 00425 } 00426 } 00427 00428 // ------------------------------------------------------------ 00429 void Marker::setCenter(int x, int y, bool relative) 00430 { 00431 if(relative) { 00432 x1 += x; y1 += y; 00433 } 00434 else { 00435 x1 = x; y1 = y; 00436 } 00437 } 00438 00439 // ------------------------------------------------------- 00440 void Marker::Bounding(int& _x1, int& _y1, int& _x2, int& _y2) 00441 { 00442 if(diag()) { 00443 _x1 = diag()->cx + x1; 00444 _y1 = diag()->cy + y1; 00445 _x2 = diag()->cx + x1+x2; 00446 _y2 = diag()->cy + y1+y2; 00447 } 00448 else { 00449 _x1 = x1; 00450 _y1 = y1+y2; 00451 _x2 = x1+x2; 00452 _y2 = y1; 00453 } 00454 } 00455 00456 // --------------------------------------------------------------------- 00457 QString Marker::save() 00458 { 00459 QString s = "<Mkr "; 00460 00461 for(auto i : VarPos){ 00462 s += QString::number(i)+"/"; 00463 } 00464 s.replace(s.length()-1,1,' '); 00465 //s.at(s.length()-1) = (const QChar&)' '; 00466 00467 s += QString::number(x1) +" "+ QString::number(y1) +" " 00468 +QString::number(Precision) +" "+ QString::number(numMode); 00469 if(transparent) s += " 1>"; 00470 else s += " 0>"; 00471 00472 return s; 00473 } 00474 00475 // --------------------------------------------------------------------- 00476 // All graphs must have been loaded before this function ! 00477 bool Marker::load(const QString& _s) 00478 { 00479 bool ok; 00480 QString s = _s; 00481 00482 if(s.at(0) != '<') return false; 00483 if(s.at(s.length()-1) != '>') return false; 00484 s = s.mid(1, s.length()-2); // cut off start and end character 00485 00486 if(s.section(' ',0,0) != "Mkr") return false; 00487 00488 int i=0, j; 00489 QString n = s.section(' ',1,1); // VarPos 00490 00491 unsigned nVarPos = 0; 00492 j = (n.count('/') + 3); 00493 VarPos.resize(j); 00494 00495 do { 00496 j = n.indexOf('/', i); 00497 VarPos[nVarPos++] = n.mid(i,j-i).toDouble(&ok); 00498 if(!ok) return false; 00499 i = j+1; 00500 } while(j >= 0); 00501 00502 n = s.section(' ',2,2); // x1 00503 x1 = n.toInt(&ok); 00504 if(!ok) return false; 00505 00506 n = s.section(' ',3,3); // y1 00507 y1 = n.toInt(&ok); 00508 if(!ok) return false; 00509 00510 n = s.section(' ',4,4); // Precision 00511 Precision = n.toInt(&ok); 00512 if(!ok) return false; 00513 00514 n = s.section(' ',5,5); // numMode 00515 numMode = n.toInt(&ok); 00516 if(!ok) return false; 00517 00518 n = s.section(' ',6,6); // transparent 00519 if(n.isEmpty()) return true; // is optional 00520 if(n == "0") transparent = false; 00521 else transparent = true; 00522 00523 return true; 00524 } 00525 00526 // ------------------------------------------------------------------------ 00527 // Checks if the coordinates x/y point to the marker text. x/y are relative 00528 // to diagram cx/cy. 00529 bool Marker::getSelected(int x_, int y_) 00530 { 00531 if(x_ >= x1) if(x_ <= x1+x2) if(y_ >= y1) if(y_ <= y1+y2) 00532 return true; 00533 00534 return false; 00535 } 00536 00537 // ------------------------------------------------------------------------ 00538 /* 00539 * the diagram this belongs to 00540 */ 00541 const Diagram* Marker::diag() const 00542 { 00543 if(!pGraph) return NULL; 00544 return pGraph->parentDiagram(); 00545 } 00546 00547 // ------------------------------------------------------------------------ 00548 Marker* Marker::sameNewOne(Graph *pGraph_) 00549 { 00550 Marker *pm = new Marker(pGraph_, 0, cx ,cy); 00551 00552 pm->x1 = x1; pm->y1 = y1; 00553 pm->x2 = x2; pm->y2 = y2; 00554 00555 pm->VarPos = VarPos; 00556 00557 pm->Text = Text; 00558 pm->transparent = transparent; 00559 pm->Precision = Precision; 00560 pm->numMode = numMode; 00561 00562 return pm; 00563 } 00564 00565 // vim:ts=8:sw=2:noet