#### Howdy, Stranger!

We are about to switch to a new forum software. Until then we have removed the registration on this forum.

# isn't stroke strange?

edited May 2014

First time i noticed and i really don't care :)

But isn't it strange that a stroke starts on the inside and ends on the outside?

stroke(255, 0, 0); rect(0, 0, width, height);

• edited May 2014

Coordinates width & height are outside canvas already! Why? B/c 1st coordinate is `0`!
Let's say that canvas is `size(800, 600);`. It means coordinate x is from `0` to `799` & y is from `0` to `599`!
To prove that correctly, turn off anti-aliasing: `noSmooth();` or `smooth(0);`before issuing rect()! ;)

• Yeah but a rect width a width of 100 with noStroke has a width of 100. A rect width a width of 100 with a stroke has a width of 101. Wouldn't it make more sense if it had a width of either 100 (inner stroke) or 102 (outer stroke) like indesign also works.

``````noSmooth();
stroke(255, 0, 0);
rect(0, 0, width, height);
``````
• edited May 2014

Like you said: perhaps the rect()'s algorithm only takes into consideration the dimensions w/o the outline stroke()!? :-/
In the example below, `rect(0, 0, width - 1, height - 1);` actually fails to fill up whole canvas: @-)

``````// forum.processing.org/two/discussion/4928/isnt-stroke-strange

noLoop();
noSmooth();
clear();

noStroke();
//stroke(#FF0000);
fill(#008000);

//rect(0, 0, width, height);
rect(0, 0, width - 1, height - 1);

/**
* Searches for color #000000 existence.
*/

println(java.util.Arrays.binarySearch(pixels, #000000));
``````
• It's logic it fails fill up the whole canvas. Yo draw smaller then the width without a stroke. Nice thing with the binarySearch, good to know :) I remember programming a method like that in the book of ben fry.

• edited May 2014

It's logic it fails fill up the whole canvas.

But `rect(0, 0, width - 1, height - 1);` wouldn't fail if `stroke(#FF0000)` is uncommented! :P

Nice thing with the binarySearch()...

It's still very exotic for me! Even though it's the 3rd time I use it! /:)

• I don't follow your sketch anymore :) What are you trying to do / show?

• edited May 2014 Answer ✓

The idea behind doing this is probably consistency with multiple objects.

If you have a rect() at x position 0, its width is 100, and then you have another rect() at x position 100 then they will share a stroke() boundary. If this was not the case then there would be a stroke() of double thickness. The case is the same on the y axis:

``````void setup() {
size(300, 300);
rect(0, 0, 100, 100);
rect(100, 0, 100, 100);
rect(0, 100, 100, 100);
rect(100, 100, 100, 100);
}
``````

What is more typical for filling the entire screen is background()

Edit: Perhaps another design choice was that this way the only special case is when an object is at the far right or bottom rather than every time objects are near each other

• edited May 2014

What are you trying to do / show?

Exactly what u had pinpointed: w/ noStroke(), rect()'s coords go from `0` to `(width & height) - 1`. Which is correct!
W/ stroke() active, it's from `0` to `width & height`. Which is 1 pixel more beyond canvas! 3:-O

And consequently, rect() is lying to us about its dimension parameters!
We coulda asked for a `100x100`, but it ends up rendering `101x101`!!! :-w

• Good answer asimes. never thought of it that way :)

• edited January 2018

I work on a algorithm at the moment and this thing is driving me nuts. If processing was made by me I would fix the damn thing...

Maybe it makes sense:

``````void setup() {
size(32, 32, JAVA2D);
}

void draw() {

stroke(0, 255, 0);
beginShape();
// Exterior part of shape, clockwise winding
vertex(4, 4);
vertex(24, 4);
vertex(24, 24);
vertex(4, 24);
//stroke(0, 255, 0);
// Interior part of shape, counter-clockwise winding
beginContour();
vertex(8, 8);
vertex(8, 16);
vertex(16, 16);
vertex(16, 8);

endContour();
//stroke(0, 0, 255);
//noStroke();
endShape(CLOSE);

if (mousePressed) {
stroke(255, 0, 0);
line(4, 4, 24, 4);
line(24, 4, 24, 24);
line(24, 24, 4, 24);
line(4, 24, 4, 4);
}

//saveFrame();
//exit();
}
``````
• the thing asimes describes has a technical name. things like this are described as "half-open" and it is to stop coincident edges being rendered twice - think what would happen if something with 50% alpha was drawn twice

• @koogs can you elaborate on that?

• it's something i remember from reading sunos graphics manuals in the 1980s (lol, old) but couldn't find a decent link last week when it came up. there's some talk about it here:

https://en.wikibooks.org/wiki/Cg_Programming/Rasterization

specifically

"There are certain rules for cases when the center of a pixel is exactly on the boundary of a primitive. These rules make sure that two adjacent triangles (i.e. triangles that share an edge) never share any pixels (unless they actually overlap) and never miss any pixels along the edge; i.e. each pixel along the edge between two adjacent triangles is covered by either triangle but not by both. This is important to avoid holes and (in case of semitransparent triangles) multiple rasterizations of the same pixel. The rules are, however, specific to implementations of GPUs. Moreover, other APIs than OpenGL may specify different rules. Thus, they won't be discussed here."

• as for this bit

think what would happen if something with 50% alpha was drawn twice

the first drawing would use 50% of the existing background colour and 50% of the new colour. the second iteration would use 50% of the new background colour (which includes the 50% of the new colour drawn on the first pass) and another 50% of the new colour. so the edge would look different from the rest of the polygon.

• Thanks.

``````void setup() {
size(300, 300);
}

void draw() {
background(255, 0, 0);
fill(50, 50);
stroke(0, 50);
triangle(20, 200, 150, 50, 150, 200);
triangle(150, 50, 150, 200, 290, 200);
}
``````

It seems to be more about avoiding double edges. So 2 adjacent 1px stroke triangles don't form a 2 pixel edge. Cause the problem you describe with the 50% is happening atm cause processing is drawing outside the shape on the right and the bottom...