3

I am making a graphing program in C++ using the SFML library. So far I have been able to draw a function to the screen. I have run into two problems along the way. The first is a line which seems to return to the origin of my the plane, starting from the end of my function.

You can see it in this image:

As you can see this "rogue" line seems to change colour as it nears the origin. My first question is what is this line and how may I eradicate it from my window?

The second problem which is slightly unrelated and more mathematical can be seen in this image:

enter image description here

As you can see the asymptotes which are points where the graph is undefined or non continuous are being drawn. This leads me to my second question: is there a way ( in code ) to identify an asymptote and not draw it to the window.

My code for anything drawn to the window is:

VertexArray axis(Lines, 4);
VertexArray curve(PrimitiveType::LinesStrip, 1000);

axis[0].position = Vector2f(100000, 0);
axis[1].position = Vector2f(-100000, 0);
axis[2].position = Vector2f(0, -100000);
axis[3].position = Vector2f(0, 100000);

float x;

for (x = -pi; x < pi; x += .0005f)
{
    curve.append(Vertex(Vector2f(x, -tan(x)), Color::Green));
}

I would very much appreciate any input : )

Update:

Thanks to the input of numerous people this code seems to work fine in fixing the asymptote problem:

for (x = -30*pi; x < 30*pi; x += .0005f)
{
    x0 = x1; y0 = y1;
    x1 = x; y1 = -1/sin(x);
    a = 0; 
    a = fabs(atan2(y1 - y0, x1 - x0));
    if (a > .499f*pi)
    {
        curve.append(Vertex(Vector2f(x1, y1), Color::Transparent));
    }
    else
    {
        curve.append(Vertex(Vector2f(x1, y1), Color::Green));
    }
}

Update 2:

The following code gets rid of the rogue line:

VertexArray curve(Lines, 1000);
float x,y;
for (x = -30 * pi; x < 30 * pi; x += .0005f)
{
    y = -asin(x);
    curve.append(Vertex(Vector2f(x, y)));
}
for (x = -30 * pi + .0005f; x < 30 * pi; x += .0005f)
{
    y = -asin(x);
    curve.append(Vertex(Vector2f(x, y)));
}
billy606
  • 71
  • 2
  • 7
  • Your rogue line looks white. Can you post the code involving white lines? – user3853544 Jan 27 '17 at 13:27
  • 1
    @user3853544 The white lines you refer to i presume are the axis. That code is included in the last section of my post. The " axis[n].pos... " part. – billy606 Jan 27 '17 at 13:29
  • In the first image south west of the origin it looks like you have a little white line thats not part of the axis. It looks as though you might have 2 rogue lines a green and a white one – user3853544 Jan 27 '17 at 13:31
  • @user3853544 Yes the rogue line is appearing without me actually typing in any code hence the name: "rogue". – billy606 Jan 27 '17 at 13:34
  • handling the jumps is not so trivial, because you might have functions that are continuous, but make a huge jump from one x-value to the next. Maybe you can set a threshold, and if the function changes by more than that threshold from positive to negative (or vice versa) in one step then dont draw the line... – 463035818_is_not_an_ai Jan 27 '17 at 13:36
  • @tobi303 I get the gist of what you're trying to say but haven't understood completely. Could you perhaps expand a little. – billy606 Jan 27 '17 at 13:46
  • @billy606 for example take y=sin(x) the max difference between consequent `y` values is `~2/dx` where dx is min or avg `x` distance between your sampled points. When you use `y=tan(x)` instead suddenly the `y` difference can grow to infinity. Then take square signal `y=x&1` then you got also infinite jumps in `y` on the edges but no asymptotes – Spektre Jan 27 '17 at 13:53
  • 1
    @billy606 Spekre has some code on this in his answer, I would just add a condition, that the consecutive y values have opposite sign just to be a bit more certain that it is a discontinuity and not just a big continuous jump – 463035818_is_not_an_ai Jan 27 '17 at 13:58
  • 1
    @billy606 btw looks like youre doing something like this: [plotting real time Data on (qwt )Oscillocope](http://stackoverflow.com/a/21658139/2521214) it might be interesting reading for you – Spektre Jan 27 '17 at 13:59
  • 1
    @Spektre oh sure. Well to be really certain that is is a discontinuity and to be certain not to mistreat a continuous part I think is quite interesting, but not so easy. Depends on what he needs. If it is just some cosmetics, anything that works is fine. On the other hand, if it is supposed to work for any kind of functions, I would actually suggest to make it a separate question – 463035818_is_not_an_ai Jan 27 '17 at 14:03
  • 1
    @Spektre after thinking about it twice... if there is a discontinuity like in `y=|tan(x)|`, then the connecting line is not visible anyhow :P – 463035818_is_not_an_ai Jan 27 '17 at 14:05
  • @Spektre and (tobi303 i couldnt tag you also) thanks for the input but I'm finding it hard to understand what you guys are proposing. Could you perhaps provide me with some code to help me to understand better. Sorry about that : ) – billy606 Jan 27 '17 at 14:17
  • @tobi303 I should go with example `y=tan(x mod (pi/2))` instead. The point is to detect the discontinuity we need to know what data you will be viewing. You can compute `ang = atan2(y[i]-y[i-1],x[i]-x[i-1])` and test `|ang| > 0.499*PI` or `|y[i]-y[i-1]|>threshold` then it is discontinuity meaning you got to split your data to separate draw calls. but each detector has its limitations and you need to tweak it to your data .... – Spektre Jan 27 '17 at 14:25
  • @Spektre one could always try to check if the derivative is not defined somewhere between `xn` and `xn+1`. I think this wouldnt be too difficult, but most likely a bit too much effort (it has to be checked for each line drawn) for just some cosmetics – 463035818_is_not_an_ai Jan 27 '17 at 14:30
  • @Spektre ...well it wont work for functions that are conitnuous but not differentiable. damn my maths isnt the best anymore, spend too much time coding only. Anyhow imho it is a very interesting question, but at the moment the biggest difficulty is that the requirements are not well defined – 463035818_is_not_an_ai Jan 27 '17 at 14:33
  • @tobi303 see edit1 in my answer (wrote directly in SO editor so there may be typos) – Spektre Jan 27 '17 at 14:41

2 Answers2

3

The first problem looks like a wrong polyline/curve handling. Don't know what API are you using for rendering but some like GDI need to start the pen position properly. For example if you draw like this:

Canvas->LineTo(x[0],y[0]);
Canvas->LineTo(x[1],y[1]);
Canvas->LineTo(x[2],y[2]);
Canvas->LineTo(x[3],y[3]);
...

Then you should do this instead:

Canvas->MoveTo(x[0],y[0]);
Canvas->LineTo(x[1],y[1]);
Canvas->LineTo(x[2],y[2]);
Canvas->LineTo(x[3],y[3]);
...

In case your API needs MoveTo command and you are not setting it then last position is used (or default (0,0)) which will connect start of your curve with straight line from last draw or default pen position.

Second problem

In continuous data you can threshold the asymptotes or discontinuities by checking the consequent y values. If your curve render looks like this:

Canvas->MoveTo(x[0],y[0]);
for (i=1;i<n;i++) Canvas->LineTo(x[i],y[i]);

Then you can change it to something like this:

y0=y[0]+2*threshold;
for (i=0;i<n;i++)
 {
 if (y[1]-y0>=threshold) Canvas->MoveTo(x[i],y[i]);
  else                   Canvas->LineTo(x[i],y[i]);
 y0=y[i];
 }

The problem is selection of the threshold because it is dependent on x density of sampled points and on the first derivation of your y data by x (slope angles)

If you are stacking up more functions the curve append will create your unwanted line ... instead handle each data as separate draw or put MoveTo command in between them

[Edit1]

I see it like this (fake split):

double x0,y0,x1,y1,a;
for (e=1,x = -pi; x < pi; x += .0005f)
    {
    // last point
    x0=x1; y0=y1;
    // your actual point
    x1=x; y1=-tan(x);
    // test discontinuity
    if (e) { a=0; e=0; } else a=fabs(atan2(y1-y0,x1-x0));
    if (a>0.499*M_PI) curve.append(Vertex(Vector2f(x1,y1), Color::Black));
     else             curve.append(Vertex(Vector2f(x1,y1), Color::Green));
    }

the 0.499*M_PI is you threshold the more closer is to 0.5*M_PIthe bigger jumps it detects... I faked the curve split by black color (background) it will create gaps on axis intersections (unless transparency is used) ... but no need for list of curves ...

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 3
    He's using [SFML](http://sfml-dev.org/) as the tags indicate, and using SFML's `sf::VertexArray` as a line strip, there's no generic way to emulate a "move to" command, unless you're starting a second draw call. – Mario Jan 27 '17 at 13:54
  • @Mario I do not use SFML but most vertex array implementations know edge flag arrays (like OpenGL) ... where you can encode The `MoveTo` – Spektre Jan 27 '17 at 13:56
  • Thankyou for your code. It fixed the asymptote problem perfectly. – billy606 Jan 27 '17 at 16:01
2

Those artifacts are due to the way sf::PrimitiveType::LinesStrip works (or more specific lines strips in general).

In your second example, visualizing y = -tan(x), you're jumping from positive infinity to negative infinity, which is the line you're seeing. You can't get rid of this, unless you're using a different primitive type or splitting your rendering into multiple draw calls.

Imagine a line strip as one long thread you're pinning with pushpins (representing your vertices). There's no (safe) way to go from positive infinity to negative infinity without those artifacts. Of course you could move outside the visible area, but then again that's really specific to this one function.

Mario
  • 35,726
  • 5
  • 62
  • 78
  • Is there another kind of primitive type ( I think that's what it is haha ) like LinesStrip which would leave me without the rogue line? – billy606 Jan 27 '17 at 14:03
  • @billy606 `sf::PrimitiveType::Lines` but this would require you to submit a start and end point for each segment, i.e. doubling your vertex count. – Mario Jan 27 '17 at 18:43