00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "Graph.h"
00011 #include "SeExprMacros.h"
00012 #ifndef SEEXPR_WIN32
00013 # include <fenv.h>
00014 #endif
00015 #include <cmath>
00016 Graph::Graph(QStatusBar* status,std::vector<GrapherExpr*>& exprs)
00017 :exprs(exprs),operationCode(NONE),rootShow(false),minShow(false),
00018 dragging(false),scaling(false),status(status)
00019 {
00020
00021 xmin=-10;xmax=10;
00022 ymin=-10;ymax=10;
00023
00024 logBase=2.;
00025 }
00026
00027 float Graph::
00028 fit(float t,float src0,float src1,float dest0,float dest1){
00029 return (t-src0)/(src1-src0)*(dest1-dest0)+dest0;
00030 }
00031
00032 void Graph::
00033 drawX(QPainter& painter,int power,bool label)
00034 {
00035 float delta=pow(logBase,power-1)/divs;
00036 float xline=ceil(xmin/delta)*delta;
00037 QString format("%1");
00038 for(;xline<xmax;xline+=delta){
00039 float x1,x2,y1,y2;
00040 xform(xline,ymin,x1,y1);
00041 xform(xline,ymax,x2,y2);
00042 if(label){
00043 painter.drawText(QPoint(x1+3,y1-5),format.arg(xline));
00044 }else{
00045 painter.drawLine(x1,y1,x2,y2);
00046 }
00047 }
00048 }
00049
00050 void Graph::
00051 drawY(QPainter& painter,int power,bool label)
00052 {
00053 QString format("%1");
00054 float delta=pow(logBase,power-1)/divs;
00055 float yline=ceil(ymin/delta)*delta;
00056 for(;yline<ymax;yline+=delta){
00057 float x1,x2,y1,y2;
00058 xform(xmin,yline,x1,y1);
00059 xform(xmax,yline,x2,y2);
00060 if(label){
00061 painter.drawText(QPoint(x1+3,y1-4),format.arg(yline));
00062 }else{
00063 painter.drawLine(x1,y1,x2,y2);
00064 }
00065 }
00066 }
00067
00068 void Graph::
00069 paintEvent(QPaintEvent *)
00070 {
00071 QPainter painter(this);
00072
00073 QBrush brush;brush.setColor(QColor(250,50,50));
00074 painter.fillRect(0,0,width(),height(),brush);
00075
00076 QPen pen;
00077 pen.setWidth(1);
00078 pen.setColor(QColor(210,210,255));
00079 painter.setPen(pen);
00080
00081 float powerx=(int)floor(log(xmax-xmin)/log(logBase));
00082 float powery=(int)floor(log(ymax-ymin)/log(logBase));
00083
00084 int powerOffsetSmall=4;
00085 int powerOffsetBig=1;
00086 drawX(painter,powerx-powerOffsetSmall);
00087 drawY(painter,powery-powerOffsetSmall);
00088
00089 pen.setWidth(2);
00090 pen.setColor(QColor(200,200,255));
00091 painter.setPen(pen);
00092
00093 drawX(painter,powerx-powerOffsetBig,false);
00094 drawY(painter,powery-powerOffsetBig,false);
00095
00096 pen.setColor(Qt::black);
00097 painter.setPen(pen);
00098 drawX(painter,powerx-powerOffsetBig,true);
00099 drawY(painter,powery-powerOffsetBig,true);
00100
00101
00102 pen.setWidth(2);
00103 pen.setColor(QColor(50,100,200));
00104 painter.setPen(pen);
00105 float x1,y1,x2,y2;
00106 xform(0,ymin,x1,y1);
00107 xform(0,ymax,x2,y2);
00108 painter.drawLine(x1,y1,x2,y2);
00109 xform(xmin,0,x1,y1);
00110 xform(xmax,0,x2,y2);
00111 painter.drawLine(x1,y1,x2,y2);
00112
00113
00114
00115
00116
00117 painter.setRenderHints(QPainter::Antialiasing);
00118
00119
00120
00121
00122 for(unsigned int i=0;i<exprs.size();i++){
00123 plotNew(painter,i);
00124 }
00125
00126 if(rootShow || minShow){
00127 pen.setColor(Qt::black);
00128 painter.setPen(pen);
00129
00130 float dx,dy;
00131 xform(rootX,rootY,dx,dy);
00132 painter.drawEllipse(QPoint(dx,dy),3,3);
00133 QString text=QString("(%1,%2)").arg(rootX).arg(rootY);
00134 painter.drawText(QPoint(dx,dy),text);
00135 }
00136
00137
00138 if(minShow || operationCode==FIND_MIN || operationCode==FIND_MAX){
00139 QBrush brush(Qt::yellow);
00140 painter.setBrush(brush);
00141
00142 xform(boundStart,ymin,x1,y1);
00143 xform(boundStart,ymax,x1,y2);
00144 xform(boundEnd,ymin,x2,y1);
00145 xform(boundEnd,ymax,x2,y2);
00146
00147
00148 painter.setOpacity(.2);
00149 painter.drawRect(x1,y1,x2-x1,y2-y1);
00150 painter.setOpacity(1);
00151 }
00152
00153 }
00154
00155
00156 void Graph::
00157 plotNew(QPainter& painter,int funcId)
00158 {
00159 GrapherExpr& expr=*exprs[funcId];
00160 if(!expr.isValid()) return;
00161
00162 QPen curvepen;
00163 QColor color=QColor(255,0,0);
00164 curvepen.setColor(color);
00165 #if 0
00166 if(funcs.isSelected(funcId))
00167 curvepen.setWidth(4);
00168 else
00169 #endif
00170 curvepen.setWidth(2);
00171
00172 float xold=xmin;
00173 QPainterPath path;
00174 bool first=false;
00175
00176 expr.setX(xold);
00177 float yold=expr.evaluate()[0];
00178 float xd,yd;
00179
00180 const bool error=false;
00181 xform(xold,yold,xd,yd);
00182 if(error) first=true;
00183 else{
00184 path.moveTo(xd,yd);
00185 first=false;
00186 }
00187
00188 float x_per_pixel=(xmax-xmin)/width(),y_per_pixel=(ymax-ymin)/height();
00189 int plotted=0;
00190 float xrate=x_per_pixel*1;
00191
00192 while(xold<xmax){
00193 bool disjoint=false;
00194 float x=xold+xrate;
00195 expr.setX(x);
00196 float y=expr.evaluate()[0];
00197 if(!error){
00198 if(fabs(y-yold)>y_per_pixel*100 && xrate>.1*x_per_pixel){xrate/=2;continue;}
00199 if(fabs(y-yold)<y_per_pixel*5 && xrate<x_per_pixel){xrate*=2;}
00200
00201 float xd,yd;
00202 xform(x,y,xd,yd);
00203 if(first || y>ymax || y<ymin){path.moveTo(xd,yd);disjoint=true;}
00204 else path.lineTo(xd,yd);
00205 }
00206 first=false;
00207 xold=x;
00208 yold=y;
00209 plotted++;
00210 }
00211 painter.strokePath(path,curvepen);
00212
00213
00214 }
00215
00216 void Graph::
00217 mousePressEvent(QMouseEvent* event)
00218 {
00219 lastcx=event->x();lastcy=event->y();
00220 if(event->button()==Qt::MidButton)
00221 dragging=true;
00222 else if(event->button()==Qt::RightButton){
00223 scaling=true;
00224 }
00225
00226 if(operationCode && Qt::LeftButton){
00227 float x,y;
00228 xforminv(lastcx,lastcy,x,y);
00229 boundStart=x;
00230 }
00231 }
00232
00233 void Graph::
00234 mouseMoveEvent(QMouseEvent* event)
00235 {
00236 if(dragging||scaling||operationCode){
00237 int newx=event->x(),newy=event->y();
00238
00239 float newx0,newy0,oldx0,oldy0,dx,dy;
00240 xforminv(newx,newy,newx0,newy0);
00241 xforminv(lastcx,lastcy,oldx0,oldy0);
00242 dx=newx0-oldx0;dy=newy0-oldy0;
00243 if(dragging){
00244
00245 xmin-=dx;xmax-=dx;
00246 ymin-=dy;ymax-=dy;
00247 }else if(scaling){
00248 float width=(xmax-xmin)/2.,height=(ymax-ymin)/2.;
00249 float xcenter=(xmax+xmin)/2.,ycenter=(ymax+ymin)/2.;
00250
00251 width*=pow(10.f,-dx/(xmax-xmin));
00252 height*=pow(10.f,-dy/(ymax-ymin));
00253 xmin=xcenter-width;
00254 ymin=ycenter-height;
00255 xmax=xcenter+width;
00256 ymax=ycenter+height;
00257 }else if(operationCode){
00258 boundEnd=newx0;
00259 }
00260 lastcx=newx;lastcy=newy;
00261 repaint();
00262 }
00263
00264 }
00265
00266 void Graph::
00267 mouseReleaseEvent(QMouseEvent* event)
00268 {dragging=false;scaling=false;
00269 if(operationCode){
00270 float x,y;
00271 xforminv(event->x(),event->y(),x,y);
00272 switch(operationCode){
00273 case NONE:
00274 break;
00275 case FIND_ROOT:
00276 solveRoot(functionIndex,x);
00277 break;
00278 case FIND_MIN:
00279 solveMin(functionIndex,boundStart,x);
00280 break;
00281 case FIND_MAX:
00282 solveMax(functionIndex,boundStart,x);
00283 break;
00284 }
00285 status->showMessage("");
00286 operationCode=NONE;
00287 }
00288 }
00289
00290 void Graph::
00291 xform(float x,float y,float& cx,float& cy)
00292 {
00293 cx=(x-xmin)/(xmax-xmin)*width();
00294 cy=height()-(y-ymin)/(ymax-ymin)*height()-1;
00295 }
00296
00297 void Graph::
00298 xforminv(float cx,float cy,float& x,float& y)
00299 {
00300 x=cx/width()*(xmax-xmin)+xmin;
00301 y=(height()-cy-1)/height()*(ymax-ymin)+ymin;
00302 }
00303
00304
00305 void Graph::
00306 scheduleRoot(const OperationCode operationCode_in,const int functionIndex_in)
00307 {
00308 operationCode=operationCode_in;
00309 functionIndex=functionIndex_in;
00310 boundStart=xmin-5;
00311 boundEnd=xmin-3;
00312 minShow=rootShow=false;
00313 repaint();
00314 }
00315
00316
00317 void Graph::
00318 solveRoot(const int function,double xInitial)
00319 {
00320 #if 0
00321 double x0=xInitial;
00322 double y_x0=funcs.eval(function,x0);
00323 double x1=xInitial+1e-5;
00324 double y_x1=funcs.eval(function,x1);
00325 for(int i=0;i<100 && fabs(x1-x0)>1e-6 && fabs(y_x0-y_x1)>1e-6;i++){
00326 double x2=x1-(x1-x0)/(y_x1-y_x0)*y_x1;
00327 x0=x1;
00328 x1=x2;
00329 y_x0=y_x1;
00330 y_x1=funcs.eval(function,x1);
00331 }
00332 rootX=x1;
00333 rootY=y_x1;
00334 minShow=false;
00335 rootShow=true;
00336 repaint();
00337 #else
00338 UNUSED(function);
00339 UNUSED(xInitial);
00340 #endif
00341 }
00342
00343
00344 double Graph::
00345 golden(const int function,double xmin,double xcenter,double xmax,bool solveMax,double tolerance)
00346 {
00347 static const double phi=.5*(1.+sqrt(5.)),resPhi=2-phi;
00348
00349
00350 double xnew=xcenter+resPhi*(xmax-xcenter);
00351
00352 if(fabs(xmax-xmin)<1e-5*(fabs(xnew)+fabs(xcenter))) return (xmax+xmin)/2;
00353
00354 #if 0
00355 double f_xcenter=funcs.eval(function,xcenter);
00356 double f_xnew=funcs.eval(function,xnew);
00357 if(solveMax){
00358 f_xcenter*=-1;
00359 f_xnew*=-1;
00360 }
00361
00362 if(f_xnew<f_xcenter) return golden(function,xcenter,xnew,xmax,solveMax,tolerance);
00363 else return golden(function,xnew,xcenter,xmin,solveMax,tolerance);
00364 #else
00365 UNUSED(function);
00366 UNUSED(solveMax);
00367 UNUSED(tolerance);
00368 #endif
00369 return 0;
00370 }
00371
00372 void Graph::
00373 solveMin(const int function,double xmin,double xmax,bool solveMax)
00374 {
00375 #if 0
00376 if(xmax<xmin) std::swap(xmin,xmax);
00377 double xsolve=golden(function,xmin,.5*(xmin+xmax),xmax,solveMax,1e-5);
00378 rootX=xsolve;
00379 rootY=funcs.eval(function,xsolve);
00380 rootShow=false;
00381 minShow=true;
00382 repaint();
00383 #else
00384 UNUSED(function);
00385 UNUSED(xmin);
00386 UNUSED(xmax);
00387 UNUSED(solveMax);
00388 #endif
00389 }
00390
00391 void Graph::
00392 solveMax(const int function,double xmin,double xmax)
00393 {
00394 solveMin(function,xmin,xmax,true);
00395 }
00396
00397 void Graph::
00398 redraw()
00399 {
00400 rootShow=false;
00401 minShow=false;
00402 repaint();
00403 }