-->

Rogue line being drawn to window

2020-04-10 09:28发布

问题:

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:

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)));
}

回答1:

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 ...



回答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.