The Y axis will be scaled automatically, depending on the values coming fallowing function.
void mouseHandleCordinate(double val){
// include graph function.
}
So I want to create the plot chart against the time. X axis represent time and Y represent the value coming above function. How I create above graph function.
Always pass the data into void mouseHandleCordinate(double val) function.
As example:
val >>> 2.1,3,1,6,7,5.5,0,9,5,6,7,3.6,2,5,6,7,8,1,2,3,4 >> represent double val
Time>>> 21,20,19,18,17,......., 4,3,2,1 second
not sure I got what you asking but looks like you want to create continuous function following table of sampled points like:
const int N=21; // number of samples
const double t0=21.0,t1=1.0; // start,end times
const double val[N]={ 2.1,3,1,6,7,5.5,0,9,5,6,7,3.6,2,5,6,7,8,1,2,3,4 };
double f(double t) // nearest
{
t = (t-t0)/(t1-t0); // time scaled to <0,1>
t*= (N-1); // time scaled to <0,N) .. index in table
int ix=t; // convert to closest index in val[]
if ((ix<0)||(ix>=N)) return 0.0; // handle undefined times
return val[ix]; // return closest point in val[]
}
This will give you the nearest neighbor style function value. If you need something better use linear or cubic or better interpolation for example:
double f(double t) // linear
{
t = (t-t0)/(t1-t0); // time scaled to <0,1>
t*= (N-1); // time scaled to <0,N) .. index in table
int ix=t; // convert to closest index in val[]
if ((ix<0)||(ix>=N)) return 0.0; // handle undefined times
if (ix==N-1) return val[ix]; // return closest point in val[] if on edge
// linear interpolation
t = t-floor(t); // distance of time between ix and ix+1 points scaled to <0,1>
return val[ix]+(val[ix+1]-val[ix])*t; // return linear interpolated value
}
Now to your problem:
void mouseHandleCordinate(double mx) // mouse x coordinate in [pixels] I assume
{
double t,x,y,x0,y0
// plot
x=0;
y=f(view_start_time)
for (t=view_start_time;t<=view_end_time;t+=(view_end_time-view_start_time)/view_size_in_pixels,x0=x,x++)
{
y0=y; y=f(t);
// render line x0,y0,x,y
}
// mouse highlight
t = view_start_time+((view_end_time-view_start_time)*mx/view_size_in_pixels);
x = mx;
y = f(t);
// render point x,y ... for example with circle r = 16 pixels
}
where:
view_start_time is time of the left most pixel in your plot view
view_end_time is time of the right most pixel in your plot view
view_size_in_pixels is the x resolution in [pixels] of your plot view
[Notes]
I code the stuff directly in SO editor so there may be typos ...
Hoping you are calling the mouseHandleCordinate in some Paint event instead of on mouse movement which should only schedule repaint order... Also you should add y view scale in similar manner to x scale I used ...
For more info see:
How can i produce multi point linear interpolation?
Related
To calculate the shortest distance from a point to the circumference of an ellipse, I have to calculate the normal/angle that passes through this point. See the following illustration:
The math to calculate this normal is explained here. In short:
Where E is the ellipse and L is the line between the point p and the circumference that creates the normal.
In C++, I have the following code fragment, where I need to insert this formula - solving it to variable t:
#include <cmath>
/// Calculate the normal of a line from ellipse circumference to point.
///
/// #param ew The width of the ellipse
/// #param eh The height of the ellipse
/// #param x The relative x position (relative to ellipse center)
/// #param y The relative y position (relative to ellipse center)
/// #return The normal in radians.
///
auto calculateNormal(float ew, float eh, float x, float y) -> double {
// ???
return std::atan(...);
};
How can I implement the formula in C++ to get the normal as shown?
What you currently have is not yet solved system of equations which as I originally taught from experience with similar but slightly simpler problem the algebraic solution leads to horrible stuff that would be too slow and inaccurate to compute (math solver throw about 2 pages of equations for roots which I throw away right away) so I would attack this with search approach instead.
If you are worrying about speed due to using goniometrics you can avoid that completely by simply computing normal as numeric derivation of the circumference points and just compare the slopes between normal (nx,ny) and found_point(x,y) - input_point(px,py).
The algo is like this:
search x in range <-a,+a>
compute y=f(x) and y1=f(x+epsilon)
I simply rewrite the implicit ellipse equation into something like this:
float ellipse_y(float rx,float ry,float x) // y = f(x)
{
if (fabs(x)>rx) return 0.0
return sqrt((rx*rx)-(x*x))*ry/rx;
}
of coarse the result should be +/- depending on the quadrant so if py<0 use negative values...
The epsilon should be some small value but not too small, I used 0.001*rx where rx,ry are sizes of the ellipse half axises.
compute normal (nx,ny)
so simply take two consequent points (x,y) and (x+epsilon,y1) substract them and rotate by 90deg by exchanging their coordinates and negate one of them. Once put together I got this:
void ellipse_n(float rx,float ry,float &nx,float &ny,float x,float &y) // x',y',y = f(x)
{
if (x<=-rx){ y=0.0; nx=-1.0; ny=0.0; return; }
ny=x+(0.001*rx); // epsilon
if (ny>=+rx){ y=0.0; nx=+1.0; ny=0.0; return; }
y=ellipse_y(rx,ry,x); // first point
nx=y-ellipse_y(rx,ry,ny); // second point
ny=ny-x;
/*
// normalize
x=divide(1.0,sqrt((nx*nx)+(ny*ny)));
nx*=x;
ny*=x;
*/
}
The normalization is optional (I commented it out for speed as its not needed for the search itself).
compute error e for the search
Simply the slopes (x-px,y-py) and (nx,ny) should be equal so:
e=fabs(((y-py)*nx)-((x-px)*ny));
The x search should minimize the e towards zero.
Do not forget to handle py<0 by negating y. Putting all togetherusing my approx search leads to:
//---------------------------------------------------------------------------
float ellipse_y(float rx,float ry,float x) // y = f(x)
{
if (fabs(x)>rx) return 0.0;
return sqrt((rx*rx)-(x*x))*ry/rx;
}
//---------------------------------------------------------------------------
void ellipse_pn(float rx,float ry,float &nx,float &ny,float x,float &y) // x',y',y = f(x) if (py>=0)
{
if (x<=-rx){ y=0.0; nx=-1.0; ny=0.0; return; }
ny=x+(0.001*rx); // epsilon
if (ny>=+rx){ y=0.0; nx=+1.0; ny=0.0; return; }
y=ellipse_y(rx,ry,x); // first point
nx=y-ellipse_y(rx,ry,ny); // second point
ny=ny-x;
}
//---------------------------------------------------------------------------
void ellipse_nn(float rx,float ry,float &nx,float &ny,float x,float &y) // x',y',y = f(x) if (py<=0)
{
if (x<=-rx){ y=0.0; nx=-1.0; ny=0.0; return; }
ny=x+(0.001*rx); // epsilon
if (ny>=+rx){ y=0.0; nx=+1.0; ny=0.0; return; }
y=-ellipse_y(rx,ry,x); // first point
nx=y+ellipse_y(rx,ry,ny); // second point
ny=ny-x;
}
//---------------------------------------------------------------------------
void this_is_main_code()
{
float rx=0.95,ry=0.35; // ellipse
float px=-0.25,py=0.15; // input point
float x,y,nx,ny;
approx ax; double e;
if (py>=0.0)
{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
ellipse_pn(rx,ry,nx,ny,x,y);
e=fabs(((y-py)*nx)-((x-px)*ny));
}
x=ax.aa; y=+ellipse_y(rx,ry,x);
}
else{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
ellipse_nn(rx,ry,nx,ny,x,y);
e=fabs(((y-py)*nx)-((x-px)*ny));
}
x=ax.aa; y=-ellipse_y(rx,ry,x);
}
// here (x,y) is found solution and (nx,ny) normal
}
//---------------------------------------------------------------------------
You still can adjust the search parameters (step and recursions) to have desired precision and speed. Also you can optimize by inlinening and removing heap thrashing and moving the range checks before the iterations...
Also you should adjust the normal computation for range <rx-epsilon,rx> as it truncates it to x=+rx (you could use negative epsilon for that).
[Ed1t1]
If I see it right the point with matching normal is also closest points so we can search directly that (which is even faster):
// search closest point
if (py>=0.0)
{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
y=ellipse_y(rx,ry,x);
x-=px; y-=py; e=(x*x)+(y*y);
}
x=ax.aa; y=+ellipse_y(rx,ry,x);
}
else{
for (ax.init(-rx,+rx,0.25*rx,3,&e);!ax.done;ax.step())
{
x=ax.a;
y=-ellipse_y(rx,ry,x);
x-=px; y-=py; e=(x*x)+(y*y);
}
x=ax.aa; y=-ellipse_y(rx,ry,x);
}
Also I feel there still might be some better solution using graphic approach like rescale to circle solve for circle and then scale back to ellipse +/- some corrections however too lazy to try...
I am trying to make a 1D fractal noise function. I have a function generating every single individual graph, but am struggling with how to add them together. I am following this tutorial
https://web.archive.org/web/20160530124230/http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
Here is my code for my final noise function
(I am using sfml, which is what the sf::vector2f are. It's just a vector of two floats, representing a coordinate.)
void fractalNoise() {
std::vector<sf::Vector2f> allGraphs;
std::vector<sf::Vector2f> singleNoise;
float persistance = 0.8; //represents the decrease of amplitude with frequency.
//The closer to one, the less the amplitude decreases each iteration
int nOOPM1 = 10; //number of iterations
for (int i = 0; i < nOOPM1; i++) {
float frequency = pow(2, i);
float amplitude = pow(persistance, I);
//generate a random plots of noise, equidistant on the x, and random on the Y.
//the 3 is the interpolation method(ignore this), and the 1000 is how many points to draw
singleNoise = this->interpolateNoise(
this->generateNoise(frequency, 300 * amplitude), 3, 1000);
between each point.
allGraphs.insert(allGraphs.end(), singleNoise.begin(), singleNoise.end());
}
this->noiseGenerated = allGraphs;
//every pixel stored in noiseGenerated is rendered to a window
};
I understand that the allGraphs.insert is just putting the next graph after the current one, but I am unsure how to add each graph together. Because of the nature of fractal noise, and the fact my frequencies are always changing, I can't just add the noise points before interpolating them, as they will mostly have different x values
Any help would be appreciated
In the BGI library's "graphics.h" header there is a function pieslice in that header file,its syntax is:
#include <graphics.h>
void pieslice(int x, int y, int stangle, int endangle, int radius);
[x,y are the center of the circle,stangle and endangle are the starting and end angles respectively]
Can we make a pieslice in C/C++ without using this in-built function of the BGI library.Please help. Tried making it with the help of the lines and mid-point circle generation algorithms.
My code so far:
#include<stdio.h>
#include<graphics.h>
static const double PI =3.141592
int main()
{
int gd=DETECT,gm;
initgraph(&gd,&gm,NULL);
int xc,yc,r,st_angle,ed_angle,k;
printf("Enter the centers of pieslice:\n");
scanf("%d %d",&xc,&yc);
printf("Enter the radius:\n");
scanf("%d",&r);
printf("Enter the starting angle:\n");
scanf("%d",&st_angle);
printf("Enter the end angle:\n");
scanf("%d",&ed_angle);
for(k=st_angle; k<=ed_angle;k++)
{
double radians =(PI /180.0) * k;
int X = xc+ cos(radians) * r;
int Y = yc+ sin(radians) * r;
putpixel(x,y,WHITE);
delay(5000);
}
void wait_for_char()
{
//Wait for a key press
int in = 0;
while (in == 0) {
in = getchar();
}
}
getch();
}
I was able to do the calculation part where i used the parametric equation of circle,but unable to generate the figure using the graphics.h function. Some help would be nice. Thank you in advance.
While running this program,i am getting this error:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
a.out: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
a.out: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
Aborted (core dumped)
why not use vectors?
So (0,0) centered pie of radius r is determined by:
u = (cos(a0),sin(a0))
v = (cos(a1),sin(a1))
x^2 + y^2 <= r^2 // circle
(x,y) x u -> CW
(x,y) x v -> CCW
the CW/CCW is determined by computing 3D cross product and examining the sign of results z coordinate...
so process all pixels in circle outscribed square and render all pixels that complies all 3 conditions.
Something like this:
void pie(int x0,int y0,int r,int a0,int a1,DWORD c)
{
// variables
int x, y, // circle centered point
xx,yy,rr, // x^2,y^2,r^2
ux,uy, // u
vx,vy, // v
sx,sy; // pixel position
// my Pixel access (remove these 3 lines)
int **Pixels=Main->pyx; // Pixels[y][x]
int xs=Main->xs; // resolution
int ys=Main->ys;
// init variables
rr=r*r;
ux=double(r)*cos(double(a0)*M_PI/180.0);
uy=double(r)*sin(double(a0)*M_PI/180.0);
vx=double(r)*cos(double(a1)*M_PI/180.0);
vy=double(r)*sin(double(a1)*M_PI/180.0);
// render |<-- remove these -->|
for (y=-r,yy=y*y,sy=y0+y;y<=+r;y++,yy=y*y,sy++) if ((sy>=0)&&(sy<ys))
for (x=-r,xx=x*x,sx=x0+x;x<=+r;x++,xx=x*x,sx++) if ((sx>=0)&&(sx<xs))
if (xx+yy<=rr) // inside circle
if ((x*uy)-(y*ux)<=0) // x,y is above a0 in clockwise direction
if ((x*vy)-(y*vx)>=0) // x,y is below a1 in counter clockwise direction
Pixels[sy][sx]=c; // change for putpixel
}
However I do not use BGI so just change the Pixels[sy][sx]=c; with your putpixel(sx,sy,c); and remove obsolete range check ifs for sx,sy. Also remove the resolution xs,ys and Pixels variables.
Here preview for (xs2,ys2 is mine middle of screen):
pie(xs2,ys2,ys2-200,10,50,0x00FF0000);
Note that I have 32 bit RGB color instead of your indexed 8 bit ones and angles are in degrees. Also note that mine y axis points down so incrementing angle is going clockwise starting from the x axis (pointing to right)
This however works only for pies below 180 degrees. For bigger ones you need to invert the cross product conditions to render when not inside the not filled pie part instead something like this:
void pie(int x0,int y0,int r,int a0,int a1,DWORD c) // a0 < a1
{
// variables
int x, y, // circle centered point
xx,yy,rr, // x^2,y^2,r^2
ux,uy, // u
vx,vy, // v
sx,sy; // pixel position
// my Pixel access
int **Pixels=Main->pyx; // Pixels[y][x]
int xs=Main->xs; // resolution
int ys=Main->ys;
// init variables
rr=r*r;
ux=double(r)*cos(double(a0)*M_PI/180.0);
uy=double(r)*sin(double(a0)*M_PI/180.0);
vx=double(r)*cos(double(a1)*M_PI/180.0);
vy=double(r)*sin(double(a1)*M_PI/180.0);
// handle big/small pies
x=a1-a0;
if (x<0) x=-x;
// render small pies
if (x<180)
{
for (y=-r,yy=y*y,sy=y0+y;y<=+r;y++,yy=y*y,sy++) if ((sy>=0)&&(sy<ys))
for (x=-r,xx=x*x,sx=x0+x;x<=+r;x++,xx=x*x,sx++) if ((sx>=0)&&(sx<xs))
if (xx+yy<=rr) // inside circle
if (((x*uy)-(y*ux)<=0) // x,y is above a0 in clockwise direction
&&((x*vy)-(y*vx)>=0)) // x,y is below a1 in counter clockwise direction
Pixels[sy][sx]=c;
}
else{
for (y=-r,yy=y*y,sy=y0+y;y<=+r;y++,yy=y*y,sy++) if ((sy>=0)&&(sy<ys))
for (x=-r,xx=x*x,sx=x0+x;x<=+r;x++,xx=x*x,sx++) if ((sx>=0)&&(sx<xs))
if (xx+yy<=rr) // inside circle
if (((x*uy)-(y*ux)<=0) // x,y is above a0 in clockwise direction
||((x*vy)-(y*vx)>=0)) // x,y is below a1 in counter clockwise direction
Pixels[sy][sx]=c;
}
}
pie(xs2,ys2,ys2-200,50,340,0x00FF0000);
The code can be further optimized for example x*uy can be changed to addition in for cycle like for(...,xuy=x*uy;...;...,xuy+=uy) eliminating slow multiplication from inner loops. The same goes for all 4 therms in the cross product conditions.
[edit1] To be more clear we have something like this:
for (x=-r,xx=x*x,sx=x0+x;x<=+r;x++,xx=x*x,sx++)
{
if (...(x*uy)...) { do something }
}
the (x*uy) is computed on each iteration of x. The x is incrementing so we can compute the value of (x*uy) from the previous value ((x-1)*uy)+uy which does not need multiplication as ((x-1)*uy) is the value from last iteration. So adding single variable that holds it can get rid of the repeated multiplication:
int xuy; // ******** *******
for (x=-r,xx=x*x,sx=x0+x,xuy=x*uy;x<=+r;x++,xx=x*x,sx++,xuy+=uy)
{
if (...(xuy)...) { do something }
}
so the initial multiplication is done just once and from then its just addition ...
Also this way of rendering is fully parallelisable...
For a better unserstanding what I've done and what I've want to achive:
I want to set up a QwtPlot with a time Axis to display a signal (Voltage over time). I wrote a own implementation for the QwtScaleDraw so i can add an offset (lets say 1000µs) and change the "zero" value of the time axis.
No my system measures a event after 123µs plus this offset. So i want to display only this part of the plot with setInterval(1000, 1123) . But for better unserstanding of the plot I want to add some time before and after this event. Lets say 10% of the event length
10% of 123µs is 12.3µs => setInterval(987.7, 1135.3)
The axis show now a timespan from -12.3µs to 135.3µs.
NOW THE QUESTION: How could I force the axis to show a major tick at 0µs?
Do I have to use the QwtScaleEngine to calculate my ticks or is it a job for the QwtScaleDraw or is there already a function inside the Axis I've only missed?
EDIT:
I think the problem is my Offset. I substract this value direct from the original value inside the Function QwtText label(qreal value) const of my own implementation of QwtScaleDraw. So the zero to display is in this example the value 1000. Maybe its better so set the offset a other way?
I found a solution:
Create a own class inherit QwtLinearScaleEngine. Add a new member variable offset and add getter/setter. Overwrite the function divideScale and test if the given interval contains your offset. If not or when offset is null, use normal function of QwtLinearScaleEngine to calculate the divide scale.
When the interval contains your offset, go from that offset back (substract) with the given step size to the lower bound. Now you can go from this value up to the upper bound and add every value as a tick.
QwtScaleDiv QwtLinearScaleEngineEx::divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize) const
{
QwtScaleDiv div = QwtLinearScaleEngine::divideScale(x1, x2, maxMajorSteps, maxMinorSteps, stepSize);
QVector<QwtScaleDiv::TickType> tickTypes = QVector<QwtScaleDiv::TickType>() << QwtScaleDiv::MajorTick << QwtScaleDiv::MediumTick << QwtScaleDiv::MinorTick;
QList<double> ticksByType[QwtScaleDiv::NTickTypes];
if(offset() == 0 || !div.contains(offset())){
for(auto type : tickTypes){
ticksByType[type] = div.ticks(type);
}
}else{
for(auto type : tickTypes){
auto ticks = div.ticks(type);
if(ticks.length() > 1){
stepSize = qAbs(ticks[1]-ticks[0]);
for( double i=offset()-(stepSize*floor((offset()-div.lowerBound())/stepSize));
i<div.upperBound();
i+=stepSize )
{
ticksByType[type].append(i);
}
}
}
}
return QwtScaleDiv(QwtInterval(x1, x2), ticksByType);
}
I am attempting to display the plot values of different points on my QCustomPlot in which I have a Line style of lsLine. I know i could set a mouse over signal on the QCustomPlot but that wont really help since I just need to be informed when the mouse is over my plotted line.My question is is there any way to find out if the mouse is over my scatter point. Is there a signal i could connect to that would tell me when the mouse is over a scatter point ?
Reimplement QCustomPlot::mouseMoveEvent or connect to QCustomPlot::mouseMove.
Then use axes' coordToPixel to translate (cursor) pixel coords to plot coords and search nearest points in your QCPDataMap with QMap::lowerBound(cursorX).
You can easily just connect a slot to the mouseMove signal that QCustomPlot emits. You can then use QCPAxis::pixelToCoord to find the coordinate :
connect(this, SIGNAL(mouseMove(QMouseEvent*)), this,SLOT(showPointToolTip(QMouseEvent*)));
void QCustomPlot::showPointToolTip(QMouseEvent *event)
{
int x = this->xAxis->pixelToCoord(event->pos().x());
int y = this->yAxis->pixelToCoord(event->pos().y());
setToolTip(QString("%1 , %2").arg(x).arg(y));
}
when You use datetime format (including more point per second) of X axis, then pixel to coord fails.
If you want to display coordinates between points, then this is the fastest way
maybe usefull (with connected signal QCustomplot::MouseMove)
void MainWindow::onMouseMoveGraph(QMouseEvent* evt)
{
int x = this->ui->customPlot->xAxis->pixelToCoord(evt->pos().x());
int y = this->ui->customPlot->yAxis->pixelToCoord(evt->pos().y());
qDebug()<<"pixelToCoord: "<<data.key<<data.value; //this is correct when step is greater 1 second
if (this->ui->customPlot->selectedGraphs().count()>0)
{
QCPGraph* graph = this->ui->customPlot->selectedGraphs().first();
QCPData data = graph->data()->lowerBound(x).value();
double dbottom = graph->valueAxis()->range().lower; //Yaxis bottom value
double dtop = graph->valueAxis()->range().upper; //Yaxis top value
long ptop = graph->valueAxis()->axisRect()->top(); //graph top margin
long pbottom = graph->valueAxis()->axisRect()->bottom(); //graph bottom position
// result for Y axis
double valueY = (evt->pos().y() - ptop) / (double)(pbottom - ptop)*(double)(dbottom - dtop) + dtop;
//or shortly for X-axis
double valueX = (evt->pos().x() - graph->keyAxis()->axisRect()->left()); //graph width in pixels
double ratio = (double)(graph->keyAxis()->axisRect()->right() - graph->keyAxis()->axisRect()->left()) / (double)(graph->keyAxis()->range().lower - graph->keyAxis()->range().upper); //ratio px->graph width
//and result for X-axis
valueX=-valueX / ratio + graph->keyAxis()->range().lower;
qDebug()<<"calculated:"<<valueX<<valueY;
}
}