Problem with Stroke Transparency in Plotting Points

I have encountered a problem with the alpha color component when experimenting with plotting points to represent Collatz orbits. See Wikipedia: Collatz conjecture.

This code does what is expected:

# plots Collatz orbits for numbers 1 to width - 1
def setup():
    size(360, 360)
    background(0)
    noLoop()
    noFill()
    strokeWeight(1)
    
    # stroke with and without transparency
    # stroke(255, 254) # -> misses some points
    stroke(255, 255) # -> draws all points
    
    noSmooth()

def draw():
    # plot each x, y where is y is in the orbit of x
    for x in range(1, width):
        c = collatz(x)
        for y in c:
            point(x, y)

def collatz(n):
    # returns Collatz orbit for n, as a list
    c = [n]
    # terminate orbit at 1
    while n > 1:
        if n % 2 == 0:
            # even number
            n //= 2
        else:
            # odd number
            n = 3 * n + 1
        c.append(n)
    return c

Here’s the result:
collatz_orbits_alpha_255

Note this statement:

    stroke(255, 255) # -> draws all points

If I comment out that statement and uncomment the previous one to use this statement, which reduces alpha to 254, the result is quite different:

    stroke(255, 254) # -> misses some points

The result becomes:
collatz_orbits_alpha_254
Many points are missing.

I have performed much experimentation, and found ways to work around this problem. For instance, using squares consisting of a single point, utilizing fill with transparency and no stroke, produces the desired result. However, I would still like to understand why reducing the alpha component from 255 to 254, when plotting points, causes the observed problem.

In case it matters, I am doing this on an old MacBook Air with El Capitan.

The title of this discussion was edited on July 20, 2021.

It seems this issue isn’t restricted to Python mode. These two code listings produce the same result (depicted below):

Java mode

size(360, 360);
background(0);
stroke(255, 254);
noSmooth();

for(int x=0; x<5000; x++) {
    point(random(width), random(height));
}

Python mode

size(360, 360)
background(0)
stroke(255, 254)
noSmooth()

for x in range(5000):
    point(random(width), random(height))

Interestingly, it’s the same areas of the screen (as your sketch) that are blank:

Screenshot from 2021-07-18 17-05-56

I deleted the noSmooth(), which solved the problem – I figured it’s something to do with how Processing handles strokes. So I re-added noSmooth() and tried a strokeCap(PROJECT), which works.

1 Like

Thanks, that does the job. Just to be safe, I took a close look to make sure it draws points at the correct pixels, rather than at neighboring ones, and it did place the points at the correct locations.

Yes, since it’s Jython, and Processing is based on Java, that seems to make sense.

Since p5.js is JavaScript, independent of Processing Java Mode, I translated it to p5.js. Here is the code without strokeCap(PROJECT):

// Plot Collatz Orbits
function setup() {
    createCanvas(360, 360);
    background(0);
    noLoop();
    noFill();
    strokeWeight(1);
    
    // stroke with and without transparency
    stroke(255, 254);
    // stroke(255, 255);
    
    noSmooth();
}

function draw() {
    // plot each x, y where is y is in the orbit of x
    for (let x = 1; x < width; x += 1) {
        c = collatz(x);
        for (let i = 0; i < c.length; i += 1) {
            point(x, c[i]);
        }
    }
}

function collatz(n) {
    // returns Collatz orbit for n, as an Array
    let c = [n];
    // terminate orbit at 1
    while (n > 1) {
        if (n % 2 == 0) {
            // even number
            n = n / 2;
        } else {
            // odd number
            n = 3 * n + 1;
        }
        c.push(n);
    }
    return c;
}

As expected, it also works.

1 Like

@javagar: Yes, @tabreturn is right.

FWIW It seems that point in the various ports of Processing doesn’t handle the cap in the same way.

I still think it is odd, that you see such a big difference between stroke(255, 255) and stroke(255, 254).
Since the Python version needs to be consistent with itself - maybe it is a bug?

In any case, the documentation of point in Processing.py ought to say something about caps.

1 Like

Yes, the sketch with alpha set to 254 should hardly look different from the one with alpha set to 255.

I had tried much lower alpha values, with the same unfortunate results as with a value of 254. I was plotting points using a variety strokeWeight settings, in order to play with translucent overlaps, and when I chose a strokeWeight of 1, the problem became evident.