A Better J2ME fillTriangle

I recently wrote about my alternative fillTriangle method.

Pattern Fill

The reason I created this method was to support older devices that don’t have a built-in version of fillTriangle. However, the implementation could be useful on more modern devices where we need more control over the way that the trinangle is rendered. For example, the buit-in version of fillTriangle only enables us to paint triangles with a solid colour. On the other hand, my routine can be customised to to enable us to fill triangles with a pattern.

A simple example is to paint triangles with horizontal stripes. To do this, we can take my original code and replace the drawHorizontalLine method with this code:

    protected void drawHorizontalLine(int x1, int x2, int y)
    {
        // For odd numbered lines...
        if ((y & 1) == 1)
        {
            // Paint magenta.
            graphics.setColor(255,0,255);
        } else {
            // Paint yellow.
            graphics.setColor(255,255,0);
        }
        // Draw the line.
        graphics.drawLine(x1, y, x2, y);
    }

Something like this could be adapted to produce alternative patterns (which I leave as an irritating excercises for the reader).

Pixel Accuracy

In that article, I mentioned that my method isn’t pixel perfect because of rounding error. The problem is that java’s integer division always rounds down. For exmaple, using java integer arithmetic, 1 / 3 = 0 and 2 / 3 = 0. I suggested previously that one way to overcome this is to use a fixed point library. However there is another way to eliminate the rounding errors.

I noticed that the rounding errors could be overcome if we could add 0.5 to the result before rounding occurs. Of course, you can’t add a fraction using integer arithmetic! Nevertheless, with a little algebraic magic, we can achieve the same result.

At the moment we have:

result = floor(n / d)

But the correct answer is:

result = floor (n/d + 1/2)

Which is equivalent to:

result = floor( (2n + d) / 2d )

No more fractions!

This means we can change all the divisions in my original code to this new format. Thus:

sx = ax + ac_num * y / ac_den;

becomes:

sx = ax + (2 * ac_num * y + ac_den) / 2 * ac_den;

and so forth.

Here’s my updated code:

protected void fillTriangle(int ax, int ay, int bx, int by, int cx, int cy)
{
// http://www.geocities.com/wronski12/3d_tutor/tri_fillers.html

// Sort the points so that ay <= by <= cy int temp; if (ay > by)
{
temp = ax;
ax = bx;
bx = temp;
temp = ay;
ay = by;
by = temp;
}
if (by > cy)
{
temp = bx;
bx = cx;
cx = temp;
temp = by;
by = cy;
cy = temp;
}
if (ay > by)
{
temp = ax;
ax = bx;
bx = temp;
temp = ay;
ay = by;
by = temp;
}

// Calc the deltas for each edge.
int ab_num;
int ab_den;
if (by – ay > 0)
{
ab_num = (bx – ax);
ab_den = (by – ay);
}
else
{
ab_num = (bx – ax);
ab_den = 1;
}

int ac_num;
int ac_den;
if (cy – ay > 0)
{
ac_num = (cx – ax);
ac_den = (cy – ay);
}
else
{
ac_num = 0;
ac_den = 1;
}

int bc_num;
int bc_den;
if (cy – by > 0)
{
bc_num = (cx – bx);
bc_den = (cy – by);
}
else
{
bc_num = 0;
bc_den = 1;
}

// The start and end of each line.
int sx;
int ex;

// The heights of the two components of the triangle.
int h1 = by – ay;
int h2 = cy – by;

// Some calculations extracted from the loops.
int ab_num_x2 = ab_num * 2;
int ab_den_x2 = ab_den * 2;

int ac_num_x2 = ac_num * 2;
int ac_den_x2 = ac_den * 2;

int bc_num_x2 = bc_num * 2;
int bc_den_x2 = bc_den * 2;

// If a is to the left of b…
if (ax < bx) { // For each row of the top component... for (int y = 0; y < h1; y++) { sx = ax + (ac_num_x2 * y + ac_den) / ac_den_x2; ex = ax + (ab_num_x2 * y + ab_den) / ab_den_x2; drawHorizontalLine(sx, ex, ay + y); } // For each row of the bottom component... for (int y = 0; y < h2; y++) { int y2 = h1 + y; sx = ax + (ac_num_x2 * y2 + ac_den) / ac_den_x2; ex = bx + (bc_num_x2 * y + bc_den) / bc_den_x2; drawHorizontalLine(sx, ex, by + y); } } else { // For each row of the bottom component... for (int y = 0; y < h1; y++) { sx = ax + (ab_num_x2 * y + ab_den) / ab_den_x2; ex = ax + (ac_num_x2 * y + ac_den) / ac_den_x2; drawHorizontalLine(sx, ex, ay + y); } // For each row of the bottom component... for (int y = 0; y < h2; y++) { int y2 = h1 + y; sx = bx + (bc_num_x2 * y + bc_den) / bc_den_x2; ex = ax + (ac_num_x2 * y2 + ac_den) / ac_den_x2; drawHorizontalLine(sx, ex, by + y); } } } [/sourcecode]

One Response to A Better J2ME fillTriangle

  1. sk750 says:

    Thank you, I’ve added code based on your fillTriangle() method to GpsMid.

Leave a comment