Textured triangles prerequisites

I’ve started into a textured triangle rendering function again. In the
process I’ve written a few functions that I’m going to suggest adding
to SDL, simply for their utility value:

/*
Simple DirectMedia Layer
Copyright © 1997-2014 Sam Lantinga

This software is provided ‘as-is’, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
    /
    /
    /
    /
    common_float.h /
    /
    /
    /
    This file provides a set of C functions for dealing with floating-point /
    /
    numbers. */
/* For FLT_EPSILON, DBL_EPSILON and LDBL_EPSILON. */

#include <float.h>

#if defined( USE_FLT ) || ( !defined( USE_DBL ) && !defined( USE_LDBL ) )

define FLT_TYPE float

define NAMESPACE flt_

define EPSILON FLT_EPSILON

#elif defined( USE_DBL )

define FLT_TYPE double

define NAMESPACE dbl_

define EPSILON DBL_EPSILON

#else

define FLT_TYPE long double

define NAMESPACE ldbl_

define EPSILON LDBL_EPSILON

#endif

/* These are designed to provide PROPER floating-point comparisons. */

int NAMESPACE##r_eq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a <= b + range )
);
}
int NAMESPACE##r_gr( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a >= b + range )
);
}
int NAMESPACE##r_ls( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range <= b ) ==
(int)( a <= b + range)
);
}
int NAMESPACE##r_greq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range >= b ||
a >= b + range
) ?
(int)1 : (int)0
);
}
int NAMESPACE##r_ls( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range <= b ||
a <= b + range
) ?
(int)1 : (int)0
);
}

int NAMESPACE##tween( FLT_TYPE *f1, FLT_TYPE *f2, FLT_TYPE *fdest,
size_t elem_count, FLT_TYPE twval )
{
if( f1 && f2 && fdest )
{
size_t iter = 0;

    while( iter < elem_count )
    {
        fdest[ iter ] = twval * ( f2[ iter ] - f1[ iter ] ) + f1[ iter ];

        ++iter;
    }

    return( 1 );
}

return( -1 );

}

int NAMESPACE##int_constraints( size_t vertcount, FLT_TYPE *vals,
long *ldest, long *hdest )
{
if( vertcount && vals && ldest && hdest )
{
size_t i = 0;

    ldest = vals[ 0 ];
    hdest = vals[ 0 ];

    while( i < vertcount )
    {
        *ldest = ( NAMESPACE##r_gr( vals[ i ], *ldest, EPSILON ) ?

*ldest : vals[ i ] );
*hdest = ( NAMESPACE##r_ls( vals[ i ], *hdest, EPSILON ) ?
*hdest : vals[ i ] );

        *ldest -= ( NAMESPACE##r_ls( vals[ i ], *ldest, EPSILON ) ? 1 : 0 );
        *hdest += ( NAMESPACE##r_gr( vals[ i ], *hdest, EPSILON ) ? 1 : 0 );

        ++i;
    }

    return( 1 );
}

return( -1 );

}

There’s some other stuff that was at one point in the same file, but
it’s more geometric in nature so I moved it. If my textured triangle
code is eventually accepted, then SOME VERSION (this version is
designed to allow higher-accuracy implementations if so desired) of
this will make it’s way into SDL. I propose it now because I consider
it to be of general interest & utility.

Jared Maddox writes:

I’ve started into a textured triangle rendering function again. In the
process I’ve written a few functions that I’m going to suggest adding
to SDL, simply for their utility value:

[…]

/* These are designed to provide PROPER floating-point comparisons. */

int NAMESPACE##r_eq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a <= b + range )
);
}

I’m no expert on floating point, but why not:

 return (a + range >= b) && (a <= b + range);

Surely that expresses your intent better?

int NAMESPACE##r_gr( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a >= b + range )
);
}

And here:
return (a >= b + range) || (a + range <= b);

(Or even “return ! NAMESPACE##r_eq(a, b, range);”)

Unless you actually meant “greater than”:

return a > b + range;

int NAMESPACE##r_ls( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range <= b ) ==
(int)( a <= b + range)
);
}

return (a >= b + range) || (a + range <= b);

Unless you meant “less than”:

return a + range < b;

int NAMESPACE##r_greq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range >= b ||
a >= b + range
) ?
(int)1 : (int)0
);
}

return a + range >= b;

(Unless you know of compilers that do not use 1 for boolean true)

int NAMESPACE##r_ls( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range <= b ||
a <= b + range
) ?
(int)1 : (int)0
);
}

return a + range <= b;

(and rename to “lseq”?)

int NAMESPACE##tween( FLT_TYPE *f1, FLT_TYPE *f2, FLT_TYPE *fdest,
size_t elem_count, FLT_TYPE twval )
{
if( f1 && f2 && fdest )
{
size_t iter = 0;

    while( iter < elem_count )
    {
        fdest[ iter ] = twval * ( f2[ iter ] - f1[ iter ] ) + f1[ iter ];

        ++iter;
    }

    return( 1 );
}

return( -1 );

}

Why return an error code here? If anyone gets the call that wrong,
surely they aren’t going to handle the returned error code correctly
either? (And maybe call it “lerp” instead of “tween”?)

int NAMESPACE##int_constraints( size_t vertcount, FLT_TYPE *vals,
long *ldest, long *hdest )
{
if( vertcount && vals && ldest && hdest )
{
size_t i = 0;

    ldest = vals[ 0 ];
    hdest = vals[ 0 ];

    while( i < vertcount )
    {
        *ldest = ( NAMESPACE##r_gr( vals[ i ], *ldest, EPSILON ) ?

*ldest : vals[ i ] );
*hdest = ( NAMESPACE##r_ls( vals[ i ], *hdest, EPSILON ) ?
*hdest : vals[ i ] );

        *ldest -= ( NAMESPACE##r_ls( vals[ i ], *ldest, EPSILON ) ? 1 : 0 );
        *hdest += ( NAMESPACE##r_gr( vals[ i ], *hdest, EPSILON ) ? 1 : 0 );

        ++i;
    }

    return( 1 );
}

return( -1 );

}

I have not tried to figure out what this function does, but why EPSILON?
You want to check that no more than a single rounding error has happened
for values around 1 while accepting larger errors for smaller values and
no errors at all for larger values?

In fact, given that you compare with ints, I guess you might as well use
0 instead of EPSILON. Most likely only -1, 0 and 1 will give a different
result that way.

eirik

Message-ID: <87wq3tdhic.fsf at ran.eba>
Content-Type: text/plain

Jared Maddox <@Jared_Maddox> writes:

I’ve started into a textured triangle rendering function again. In the
process I’ve written a few functions that I’m going to suggest adding
to SDL, simply for their utility value:

[…]

/* These are designed to provide PROPER floating-point comparisons. */

int NAMESPACE##r_eq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a <= b + range )
);
}

I’m no expert on floating point, but why not:

 return (a + range >= b) && (a <= b + range);

Surely that expresses your intent better?

I wrote it maybe a week ago, so I don’t actually remember. It might
have been just a quick measure to ensure that negative range values
were handled correctly or something. I’ll stick in a note to review
the logic of the comparison tests.

As for the casts and parentheses, that’s the style that I prefer. My
screen is more than 40 characters wide, and my harddrive is modern
too. I have no reason to try to save characters.

int NAMESPACE##r_gr( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a >= b + range )
);
}

And here:
return (a >= b + range) || (a + range <= b);

(Or even “return ! NAMESPACE##r_eq(a, b, range);”)

Unless you actually meant “greater than”:

return a > b + range;

Greater-than. I remember that all of the comparisons were written such
that I didn’t have to properly analyze them (I wanted them out of the
way, so that I could get back to the bits that I needed them for).
Looking at it though, I think that genuinely is a bug. I’ve now
swapped == with && on both gr() and ls().

int NAMESPACE##r_ls( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )

Unless you meant “less than”:

Less-than.

int NAMESPACE##r_greq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range >= b ||
a >= b + range
) ?
(int)1 : (int)0
);
}

return a + range >= b;

(Unless you know of compilers that do not use 1 for boolean true)

I don’t know of any (I have vague memories of there being something
that will just pass values straight through, though), but that doesn’t
matter: what DOES matter is that I don’t want to do the RESEARCH when
it’s so easy to side-step the issue completely. The trinary operator
will stay regardless of the comparison that it depends on.

int NAMESPACE##r_ls( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range <= b ||
a <= b + range
) ?
(int)1 : (int)0
);
}

return a + range <= b;

(and rename to “lseq”?)

Thanks for catching that, fixed.

Just for reference, I’ve stuck a copy of your proposed versions in the
file, along with your contact info.

int NAMESPACE##tween( FLT_TYPE *f1, FLT_TYPE *f2, FLT_TYPE *fdest,
size_t elem_count, FLT_TYPE twval )
{

return( -1 );

}

Why return an error code here? If anyone gets the call that wrong,
surely they aren’t going to handle the returned error code correctly
either?

Partly stylistic, partly because a case can be made for null pointers
being a shorthand for “all elements zero”: instead of supporting that,
I indicate that it’s an error.

(And maybe call it “lerp” instead of “tween”?)

Maybe, but for the moment I’m going to keep it like this because
that’s the name that I first heard for it (in some sort of math
class).

int NAMESPACE##int_constraints( size_t vertcount, FLT_TYPE *vals,
long *ldest, long *hdest )

I have not tried to figure out what this function does,

It calculates the set of two integers that fully enclose the set of
floats that it’s given. I use it to calculate a bounding box (the
first version was actually two dimensional, and I believe used point
pairs).

but why EPSILON?
You want to check that no more than a single rounding error has happened
for values around 1 while accepting larger errors for smaller values and
no errors at all for larger values?

Because I was lazy, basically. I’ve since considered a second version
of the comparison functions to pay attention to floating-point
exponents, but I’m not currently taking the time to fix it. I’ve added
a note to the file about this.

In fact, given that you compare with ints, I guess you might as well use
0 instead of EPSILON. Most likely only -1, 0 and 1 will give a different
result that way.

Maybe, all of this stuff was basically written quick to get it out of
the way. It’ll be good to have these notes when I go back to review.
Regardless, I want to try to get this to come as close as possible: I
use it to determine “dirty rect” areas.> Date: Sat, 07 Feb 2015 13:37:47 +0100

From: Eirik Byrkjeflot Anonsen
To: SDL Development List
Subject: Re: [SDL] Textured triangles prerequisites

Jared Maddox writes:>> Date: Sat, 07 Feb 2015 13:37:47 +0100

From: Eirik Byrkjeflot Anonsen <@Eirik_Byrkjeflot_Ano>
To: SDL Development List
Subject: Re: [SDL] Textured triangles prerequisites
Message-ID: <87wq3tdhic.fsf at ran.eba>
Content-Type: text/plain

Jared Maddox writes:

I’ve started into a textured triangle rendering function again. In the
process I’ve written a few functions that I’m going to suggest adding
to SDL, simply for their utility value:

[…]

/* These are designed to provide PROPER floating-point comparisons. */

int NAMESPACE##r_eq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a <= b + range )
);
}

I’m no expert on floating point, but why not:

 return (a + range >= b) && (a <= b + range);

Surely that expresses your intent better?

I wrote it maybe a week ago, so I don’t actually remember. It might
have been just a quick measure to ensure that negative range values
were handled correctly or something. I’ll stick in a note to review
the logic of the comparison tests.

So what is the correct behaviour for negative range values? Depends on
how you define “range”, I guess. My intuitive feeling is that negative
range values should always yield not-equal. To me it would indicate that
you want less than 0 margin of error for the equality test.

Or just document that negative range values are invalid input yielding
undefined results. (Which is what I really expected.)

As for the casts and parentheses, that’s the style that I prefer. My
screen is more than 40 characters wide, and my harddrive is modern
too. I have no reason to try to save characters.

int NAMESPACE##r_gr( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a >= b + range )
);
}

And here:
return (a >= b + range) || (a + range <= b);

(Or even “return ! NAMESPACE##r_eq(a, b, range);”)

Unless you actually meant “greater than”:

return a > b + range;

Greater-than. I remember that all of the comparisons were written such
that I didn’t have to properly analyze them (I wanted them out of the
way, so that I could get back to the bits that I needed them for).
Looking at it though, I think that genuinely is a bug. I’ve now
swapped == with && on both gr() and ls().

I certainly had to “properly analyze” your comparisons to figure out
what they did. And even then I was not sure I got it right :slight_smile:

I suggested my forms precisely because I think they are more “obviously
correct” to anyone reading them. But things do get more thorny if you
will allow negative range values to have some other meaning than “more
precise than exact”. Though in that case the code (or at least a
comment) should probably make that clear, too.

Also, a general issue: I wrote my suggestions so that two numbers will
compare as exactly one of “less than”, “equal” or “greater than”. I
think that is a useful rule for “proper comparisons”. And of course,
“less than or equal” should always give the same answer as testing for
"less than" || “equal” (and same for “greater than or equal” of course).

There are some use cases where you would want different results, but I
think those should be seen as the special cases.

int NAMESPACE##r_greq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range >= b ||
a >= b + range
) ?
(int)1 : (int)0
);
}

return a + range >= b;

(Unless you know of compilers that do not use 1 for boolean true)

I don’t know of any (I have vague memories of there being something
that will just pass values straight through, though), but that doesn’t
matter: what DOES matter is that I don’t want to do the RESEARCH when
it’s so easy to side-step the issue completely. The trinary operator
will stay regardless of the comparison that it depends on.

I’m pretty sure it would be a violation of the C++03 specification to
convert boolean true to anything other than 1. I believe that also holds
for C89, but I don’t have that specification lying around to check. Then
again, I’m not sure why the function has to return exactly 0 or 1. If it
returns a C89 “boolean”, it might as well return 0 or non-zero.

But if it really is important that this function returns only 0 or 1,
and you want to be paranoid about it, I’m not going to complain :slight_smile:
(I’m generally rather paranoid myself when coding)

[…]

(And maybe call it “lerp” instead of “tween”?)

Maybe, but for the moment I’m going to keep it like this because
that’s the name that I first heard for it (in some sort of math
class).

Sure, I don’t particularly mind. I’m just thinking that quite often you
would want something else than linear interpolation when tweening.

int NAMESPACE##int_constraints( size_t vertcount, FLT_TYPE *vals,
long *ldest, long *hdest )

I have not tried to figure out what this function does,

It calculates the set of two integers that fully enclose the set of
floats that it’s given. I use it to calculate a bounding box (the
first version was actually two dimensional, and I believe used point
pairs).

[…]

Maybe, all of this stuff was basically written quick to get it out of
the way. It’ll be good to have these notes when I go back to review.
Regardless, I want to try to get this to come as close as possible: I
use it to determine “dirty rect” areas.

Then you want your margin of error to be “imperceptably outside”, I
think. If your ints here are true screen-space pixels, I guess 0.001
would work just fine. Anything that covers less than 1/1000 of a pixel
will most likely not be visible. Possibly add a factor of 10 or 100 for
added safety. Though I guess it would be better to just pass the margin
of error as an argument to the function, so the caller can decide what
is “imperceptible” for their particular use (I can see that some uses
would require 0 here).

eirik

Eirik Byrkjeflot Anonsen <@Eirik_Byrkjeflot_Ano> writes:> Jared Maddox writes:

Date: Sat, 07 Feb 2015 13:37:47 +0100
From: Eirik Byrkjeflot Anonsen <@Eirik_Byrkjeflot_Ano>
To: SDL Development List
Subject: Re: [SDL] Textured triangles prerequisites
Message-ID: <87wq3tdhic.fsf at ran.eba>
Content-Type: text/plain

Jared Maddox writes:

I’ve started into a textured triangle rendering function again. In the
process I’ve written a few functions that I’m going to suggest adding
to SDL, simply for their utility value:

[…]

int NAMESPACE##int_constraints( size_t vertcount, FLT_TYPE *vals,
long *ldest, long *hdest )

I have not tried to figure out what this function does,

It calculates the set of two integers that fully enclose the set of
floats that it’s given. I use it to calculate a bounding box (the
first version was actually two dimensional, and I believe used point
pairs).

Actually, on second thoughts I think you want:

/* Calculates the smallest integer range that will cover the

  • the floating point range ‘low’ to ‘high’ with an error margin
  • of ‘margin’. It is assumed that ‘low’ < ‘high’.
    */
    void NAMESPACE##covering_int_range(
    FLT_TYPE low,
    FLT_TYPE high,
    long *ret_low,
    long *ret_high,
    FLT_TYPE margin) {
    *ret_low = NAMESPACE##floor(low + margin);
    *ret_high = NAMESPACE##ceil(high - margin);
    }

/* Calculates the smallest integer range that will cover

  • all the floating point values in ‘vals’ with an error
  • margin of ‘margin’.
    */
    void NAMESPACE##int_bounds(
    size_t vertcount,
    FLT_TYPE *vals,
    long *ret_low,
    long *ret_high,
    FLT_TYPE margin) {
    size_t vertidx = 0;
    FLT_TYPE low, high;
    if (vertcount <= 0) {
    *ret_low = 0;
    *ret_high = 0;
    return;
    }
    low = vals[0];
    high = vals[0];
    while (vertidx < vertcount) {
    if (vals[0] < low)
    low = vals[0];
    if (vals[0] > high)
    high = vals[0];
    }
    NAMESPACE##covering_int_range(low, high, ret_low, ret_high, margin);
    }

You may want to improve the documentation. (Also, when the error margin
is greater than 0.5 you might get a larger ‘*ret_low’ than ‘*ret_high’,
but I don’t see that as a problem.) Oh, and the coding style and naming
of arguments is the way I write code. Feel free to change :slight_smile:

eirik

Message-ID: <87r3u0cxv1.fsf at ran.eba>
Content-Type: text/plain

Jared Maddox <@Jared_Maddox> writes:

Message-ID: <87wq3tdhic.fsf at ran.eba>
Content-Type: text/plain

Jared Maddox <@Jared_Maddox> writes:

I’ve started into a textured triangle rendering function again. In the
process I’ve written a few functions that I’m going to suggest adding
to SDL, simply for their utility value:

[…]

/* These are designed to provide PROPER floating-point comparisons. */

int NAMESPACE##r_eq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a <= b + range )
);
}

I’m no expert on floating point, but why not:

 return (a + range >= b) && (a <= b + range);

Surely that expresses your intent better?

I wrote it maybe a week ago, so I don’t actually remember. It might
have been just a quick measure to ensure that negative range values
were handled correctly or something. I’ll stick in a note to review
the logic of the comparison tests.

So what is the correct behaviour for negative range values?

In my eyes, the correct behavior for any range value is identical to
the behavior for abs( range ). This is why there’s a lot of mirroring
in the tests.

int NAMESPACE##r_gr( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a >= b + range )
);
}

And here:
return (a >= b + range) || (a + range <= b);

(Or even “return ! NAMESPACE##r_eq(a, b, range);”)

Unless you actually meant “greater than”:

return a > b + range;

Greater-than. I remember that all of the comparisons were written such
that I didn’t have to properly analyze them (I wanted them out of the
way, so that I could get back to the bits that I needed them for).
Looking at it though, I think that genuinely is a bug. I’ve now
swapped == with && on both gr() and ls().

I certainly had to “properly analyze” your comparisons to figure out
what they did. And even then I was not sure I got it right :slight_smile:

I suggested my forms precisely because I think they are more “obviously
correct” to anyone reading them. But things do get more thorny if you
will allow negative range values to have some other meaning than “more
precise than exact”. Though in that case the code (or at least a
comment) should probably make that clear, too.

Also, a general issue: I wrote my suggestions so that two numbers will
compare as exactly one of “less than”, “equal” or “greater than”.

Easier with ints than floats, since a single float datatype can have
different precisions depending on whether it’s in memory or a
floating-point register. I expect that these will always be a little
"fuzzy" at the dividing line between those three comparison results
for that reason, though ideally there shouldn’t be any overlap outside
of those cases.

I think that is a useful rule for “proper comparisons”. And of course,
“less than or equal” should always give the same answer as testing for
"less than" || “equal” (and same for “greater than or equal” of course).

There are some use cases where you would want different results, but I
think those should be seen as the special cases.

With the exception of “bordering numbers” I’m in agreement.

int NAMESPACE##r_greq( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range >= b ||
a >= b + range
) ?
(int)1 : (int)0
);
}

return a + range >= b;

(Unless you know of compilers that do not use 1 for boolean true)

I don’t know of any (I have vague memories of there being something
that will just pass values straight through, though), but that doesn’t
matter: what DOES matter is that I don’t want to do the RESEARCH when
it’s so easy to side-step the issue completely. The trinary operator
will stay regardless of the comparison that it depends on.

I’m pretty sure it would be a violation of the C++03 specification to
convert boolean true to anything other than 1. I believe that also holds
for C89, but I don’t have that specification lying around to check. Then
again, I’m not sure why the function has to return exactly 0 or 1.

So that this will be the only place I have to care :slight_smile: .

But if it really is important that this function returns only 0 or 1,
and you want to be paranoid about it, I’m not going to complain :slight_smile:
(I’m generally rather paranoid myself when coding)

It’s mostly like that so that I’ll never have reason to complain :wink: .

(And maybe call it “lerp” instead of “tween”?)

Maybe, but for the moment I’m going to keep it like this because
that’s the name that I first heard for it (in some sort of math
class).

Sure, I don’t particularly mind. I’m just thinking that quite often you
would want something else than linear interpolation when tweening.

Point, name changed.

int NAMESPACE##int_constraints( size_t vertcount, FLT_TYPE *vals,
long *ldest, long *hdest )

I have not tried to figure out what this function does,

It calculates the set of two integers that fully enclose the set of
floats that it’s given. I use it to calculate a bounding box (the
first version was actually two dimensional, and I believe used point
pairs).

[…]

Maybe, all of this stuff was basically written quick to get it out of
the way. It’ll be good to have these notes when I go back to review.
Regardless, I want to try to get this to come as close as possible: I
use it to determine “dirty rect” areas.

Then you want your margin of error to be “imperceptably outside”, I
think.

Yep.

If your ints here are true screen-space pixels, I guess 0.001
would work just fine. Anything that covers less than 1/1000 of a pixel
will most likely not be visible. Possibly add a factor of 10 or 100 for
added safety. Though I guess it would be better to just pass the margin
of error as an argument to the function, so the caller can decide what
is “imperceptible” for their particular use (I can see that some uses
would require 0 here).

There probably are some cases for the function itself, though for my
purposes an extra pixel of padding on all four sides is also fine.> Date: Sun, 08 Feb 2015 14:54:26 +0100

From: Eirik Byrkjeflot Anonsen
To: SDL Development List
Subject: Re: [SDL] Textured triangles prerequisites

Date: Sat, 07 Feb 2015 13:37:47 +0100
From: Eirik Byrkjeflot Anonsen
To: SDL Development List
Subject: Re: [SDL] Textured triangles prerequisites

OK, the files for the prerequisites are ready for someone to make
final changes and a patch for them. I’m not setup to compile SDL so I
won’t be doing that bit, but before I trow them onto bugzilla I want
some final-stage feedback, particularly from Sam or Ryan, since I
don’t recall anything else in SDL quite being structured like this.

/*
Simple DirectMedia Layer
Copyright © 1997-2015 Sam Lantinga

This software is provided ‘as-is’, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
    /
    /
    common_macros.h */

/* This file provides a standard set of C preprocessor macros. */

#ifndef COMMON_MACROS

define COMMON_MACROS

define IDENTITY( a ) a

define CONCAT( a, … ) PRIMITIVE_CONCAT( a, VA_ARGS )

define PRIMITIVE_CONCAT( a, … ) a ## VA_ARGS

define IFF( b ) PRIMITIVE_CONCAT( IIF_, b )

define IFF_0( t, … ) VA_ARGS

define IIF_1( t, … ) t

define COMPL( b ) PRIMITIVE_CONCAT( COMPL_, b )

define COMPL_0 1

define COMPL_1 0

define BITAND( b ) PRIMITIVE_CONCAT( BITAND_, b )

define BITAND_0( c ) 0

define BITAND_1( c ) c

#endif
/* End common_macros.h */

/*
Simple DirectMedia Layer
Copyright © 1997-2015 Sam Lantinga

This software is provided ‘as-is’, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
    /
    /
    common_float.h */

/* This file provides a set of C functions for dealing with floating-point /
/
numbers. */

#ifndef COMMON_FLOAT_H

define COMMON_FLOAT_H

ifndef NAMESPACE

error “common_float.h requires the macro NAMESPACE to be defined.”

else

/* For FLT_EPSILON, DBL_EPSILON and LDBL_EPSILON. */

include <float.h>

include “common_macros.h”

if defined( USE_FLT ) || ( !defined( USE_DBL ) && !defined( USE_LDBL ) )

define FLT_TYPE float

define EPSILON FLT_EPSILON

elif defined( USE_DBL )

define FLT_TYPE double

define EPSILON DBL_EPSILON

else

define FLT_TYPE long double

define EPSILON LDBL_EPSILON

endif

define CEIL CONCAT( NAMESPACE, ceil )

define FLOOR CONCAT( NAMESPACE, floor )

define EQ CONCAT( NAMESPACE, r_eq )

define GR CONCAT( NAMESPACE, r_gr )

define LS CONCAT( NAMESPACE, r_ls )

define GREQ CONCAT( NAMESPACE, r_greq )

define LSEQ CONCAT( NAMESPACE, r_lseq )

define TWEEN CONCAT( NAMESPACE, tween )

define INT_CONSTR CONCAT( NAMESPACE, int_constraints )

/* These are designed to provide PROPER floating-point comparisons. */

int CEIL( FLT_TYPE in, long *result );
int FLOOR( FLT_TYPE in, long *result );

/* Simple comparisons. */

int EQ ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range );
int GR ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range );
int LS ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range );

/* Compound comparisons. */

int GREQ ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range );
int LSEQ ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range );

/* Arbitrary-element interpretation. */

int TWEEN ( FLT_TYPE *f1, FLT_TYPE *f2, FLT_TYPE *fdest, size_t
elem_count, FLT_TYPE twval );

/* Arbitrary-element bounds finding. */

int INT_CONSTR ( size_t vertcount, FLT_TYPE *vals, long *ldest,
long *hdest );

endif

#endif
/* End common_float.h */

/*
Simple DirectMedia Layer
Copyright © 1997-2015 Sam Lantinga

This software is provided ‘as-is’, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
    /
    /
    common_float.xmacro */

/* This file provides a set of C functions for dealing with floating-point /
/
numbers. */

#ifndef NAMESPACE

error “common_float.xmacro requires the macro NAMESPACE to be defined.”

#else

include <limits.h>

include “common_float.h”

int CEIL( FLT_TYPE in, long *result )
{
if( result && !( (FLT_TYPE)LONG_MIN - 1 > in ) && !( LONG_MAX < in ) )
{
*result = in;

    while( *result < in )
    {
        *result += 1;
    }

    return( 1 );
}

return( -1 );

}
int FLOOR( FLT_TYPE in, long *result )
{
if( result && !( LONG_MIN > in ) && !( (FLT_TYPE)LONG_MAX + 1 < in ) )
{
*result = in;

    while( *result > in )
    {
        *result -= 1;
    }

    return( 1 );
}

return( -1 );

}

/* These are designed to provide PROPER floating-point comparisons. */

int EQ ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a <= b + range )
);
}
int GR ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range >= b ) ==
(int)( a >= b + range )
);
}
int LS ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(int)( a + range <= b ) ==
(int)( a <= b + range)
);
}
int GREQ ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range >= b ||
a >= b + range
) ?
(int)1 : (int)0
);
}
int LSEQ ( FLT_TYPE a, FLT_TYPE b, FLT_TYPE range )
{
return
(
(
a + range <= b ||
a <= b + range
) ?
(int)1 : (int)0
);
}

int TWEEN ( FLT_TYPE *f1, FLT_TYPE *f2, FLT_TYPE *fdest, size_t
elem_count, FLT_TYPE twval )
{
if( f1 && f2 && fdest )
{
size_t iter = 0;

    while( iter < elem_count )
    {
        fdest[ iter ] = twval * ( f2[ iter ] - f1[ iter ] ) + f1[ iter ];

        ++iter;
    }

    return( 1 );
}

return( -1 );

}

int INT_CONSTR ( size_t vertcount, FLT_TYPE *vals, long *ldest, long *hdest )
{
if( vertcount && vals && ldest && hdest )
{
size_t i = 0;
long high, low;

    *ldest = vals[ 0 ];
    *hdest = vals[ 0 ];

    while( i < vertcount )
    {
        if( !CEIL( vals[ i ],    &high ) )
        {
            /* Error. */
        }
        if( !FLOOR( vals[ i ],    &low ) )
        {
            /* Error. */
        }

        *ldest = (long)( GR ( low, *ldest, EPSILON ) ? *ldest : low );
        *hdest = (long)( LS ( high, *hdest, EPSILON ) ? *hdest : high );

        ++i;
    }

    return( 1 );
}

return( -1 );

}

include “end_common_float.h”

#endif
/* End common_float.xmacro */

/*
Simple DirectMedia Layer
Copyright © 1997-2015 Sam Lantinga

This software is provided ‘as-is’, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
    /
    /
    common_float.c */

/* This file provides a set of C functions for dealing with floating-point /
/
numbers. */

#include “common_macros.h”

/* Try to preserve NAMESPACE, but allow it to be overridden. */

#ifndef CMNFLT_NAMESPACE

define CMNFLT_NAMESPACE IDENTITY( NAMESPACE )

#endif

/* For float functions. */

#define USE_FLT

define NAMESPACE CONCAT( CMNFLT_NAMESPACE, flt_ )

include “common_float.xmacro”

undef NAMESPACE

#undef USE_FLT

/* For double functions. */

#define USE_DBL

define NAMESPACE CONCAT( CMNFLT_NAMESPACE, dbl_ )

include “common_float.xmacro”

undef NAMESPACE

#undef USE_DBL

/* For long-double functions. */

#define USE_LDBL

define NAMESPACE CONCAT( CMNFLT_NAMESPACE, ldbl_ )

include “common_float.xmacro”

undef NAMESPACE

#undef USE_LDBL

The rationale is fairly straightforward: if someone wants more
precision in the renderer, I want them to be able to just change some
configure flags and build. Some things are technically incorrect:
FLOOR should probably call floor() from SDL’s standard library
routines. I’m not willing to setup a, SDL build environment just to do
some testing on code that can be tested “outside”, so I wrote it as a
mirror of CEIL (did you know that SDL doesn’t seem to have a ceil()
fallback?) instead.

This stuff (though I forget whether it was all or just most) gets used
in the functions that I’m working on for textured triangles, so if my
renderoing code is going to be used for that then some form of these
functions needs to be in place.

May be a good idea to put this on Bugzilla I guess (even if it’s code
that needs modifications before being applied onto SDL).

Message-ID:
<CAEyBR+V5o8Uy5sNxzFyuqnJ1H4mXYXUcE4H_3eMVxc=0wTy7kA at mail.gmail.com>
Content-Type: text/plain; charset=UTF-8

May be a good idea to put this on Bugzilla I guess (even if it’s code
that needs modifications before being applied onto SDL).

Entered as: https://bugzilla.libsdl.org/show_bug.cgi?id=2888

I have the renderer code in an email waiting for moderator approval,
but I haven’t finished checking that it compiles yet.> Date: Mon, 23 Feb 2015 05:19:35 -0300

From: Sik the hedgehog <sik.the.hedgehog at gmail.com>
To: SDL Development List
Subject: Re: [SDL] Textured triangles prerequisites