Rectangle >> intersects: "degenerate case" -- *wrong* behavior??

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Rectangle >> intersects: "degenerate case" -- *wrong* behavior??

Jim Rosenberg
[Apologies in advance if this has been discussed up here before ...]

Grumble grumble grumble grumble. Grrr!

Consider the case of rectangle a and rectangle b where a happens to be
"degenerate" in the sense that it has 0 area -- let's say that a origin y =
a corner y -- i.e. if you draw a it looks like a horizontal line. And let's
say that if you draw these two rectangles, a "goes inside the area of" b.
Do they intersect? I'd sure say yes. Squeak 3.8 says yes. Squeak 4.3 says
no. *BUT* a intersect: b in Squeak 4.3 *does return* the part of a that's
inside b.

Looking at the code for Rectangle >> intersect: in 4.3, it sure looks to me
like this behavior was introduced rather explicitly and deliberately.

But is it correct? I'd say no. I'd say no-brainer no. I guess maybe
somebody is thinking that Rectangle >> intersects: should produce an answer
that agrees with the question: does a possible intersection have area
greater than 0? Well, sorry, but I don't think that's the right question.
The right question is: do the two rectangles have points in common?

I have code that was very mysteriously broken by this; I've created a
degenerate rectangle out of a line *on purpose* just to be able to get the
intersection of a line and a rectangle. I don't see any reason why I
shouldn't be able to do this. Set theory says I can do it. Geometry says I
can do it. Squeak 3.8 says I can do it.

So: whichever committer out there decided that a degenerate rectangle can't
intersect, what exactly will I break if I put back the [correct, ahem ...]
3.8 behavior of Rectangle >> intersect: in 4.3?

I say: put the old behavior back. It wasn't wrong. The new behavior is
wrong.

-Thanks, Jim

Reply | Threaded
Open this post in threaded view
|

Re: Rectangle >> intersects: "degenerate case" -- *wrong* behavior??

Nicolas Cellier
Well, the old behavior of intersects: was wrong with respect to intersect:

This is because intersect: answer an "empty" rectangle (negative width
or height).
Such intersection HAS TO BE EMPTY, and thus cannot intersects: anything...

To accomodate the degenerated case (one null dimension, the other
positive or null) we can change the final tests to strict
inequalities. OK?

Nicolas

2013/1/15 Jim Rosenberg <[hidden email]>:

> [Apologies in advance if this has been discussed up here before ...]
>
> Grumble grumble grumble grumble. Grrr!
>
> Consider the case of rectangle a and rectangle b where a happens to be
> "degenerate" in the sense that it has 0 area -- let's say that a origin y =
> a corner y -- i.e. if you draw a it looks like a horizontal line. And let's
> say that if you draw these two rectangles, a "goes inside the area of" b. Do
> they intersect? I'd sure say yes. Squeak 3.8 says yes. Squeak 4.3 says no.
> *BUT* a intersect: b in Squeak 4.3 *does return* the part of a that's inside
> b.
>
> Looking at the code for Rectangle >> intersect: in 4.3, it sure looks to me
> like this behavior was introduced rather explicitly and deliberately.
>
> But is it correct? I'd say no. I'd say no-brainer no. I guess maybe somebody
> is thinking that Rectangle >> intersects: should produce an answer that
> agrees with the question: does a possible intersection have area greater
> than 0? Well, sorry, but I don't think that's the right question. The right
> question is: do the two rectangles have points in common?
>
> I have code that was very mysteriously broken by this; I've created a
> degenerate rectangle out of a line *on purpose* just to be able to get the
> intersection of a line and a rectangle. I don't see any reason why I
> shouldn't be able to do this. Set theory says I can do it. Geometry says I
> can do it. Squeak 3.8 says I can do it.
>
> So: whichever committer out there decided that a degenerate rectangle can't
> intersect, what exactly will I break if I put back the [correct, ahem ...]
> 3.8 behavior of Rectangle >> intersect: in 4.3?
>
> I say: put the old behavior back. It wasn't wrong. The new behavior is
> wrong.
>
> -Thanks, Jim
>

Reply | Threaded
Open this post in threaded view
|

Re: Rectangle >> intersects: "degenerate case" -- *wrong* behavior??

Jim Rosenberg
--On Tuesday, January 15, 2013 01:49:26 +0100 Nicolas Cellier
<[hidden email]> wrote:

> Well, the old behavior of intersects: was wrong with respect to intersect:
>
> This is because intersect: answer an "empty" rectangle (negative width
> or height).
> Such intersection HAS TO BE EMPTY, and thus cannot intersects: anything...
>
> To accomodate the degenerated case (one null dimension, the other
> positive or null) we can change the final tests to strict
> inequalities. OK?

Hmm ...

I guess changing to strict inequality would be OK with *me* -- as far as
not breaking any of my own code -- but I'm still wondering whether silently
returning false from Rectangle >> intersects: in the case of "negative
height" or "negative width"  is the right behavior.

The issue here, as I understand it, is that a "rectangle" where the origin
and corner are not top-left and bottom-right is "incorrectly specified".
So, if Rectangle >> intersects: can't really handle an incorrectly
specified rectangle, should it return false, or throw an error?

Of course changing the behavior to throw an error could break somebody
else's code, which I wouldn't want to do.

What should Rectangle origin: (25@250) corner: (100@100) produce? A
rectangle starting from (25@250) and ending at (100@100) certainly makes
semantic sense. But it's "incorrectly specified". Should it throw an error?
Should it produce the same thing as
Rectangle origin: (25@100) corner: (100@250)? Should there be a new class
method, say Rectangle from: (25@250 to: (100@100) which would be guaranteed
to produce the rectangle with origin (25@100) and corner (100@250)? Should
Rectangle >> intersects: "normalize" both the receiver and argument before
calculating whether things intersect?

What "is" a rectangle? How is the Squeak user supposed to know? There are
at least two interpretations of what "rectangle" might mean:

(1) a 4-sided polygon where all 4 sides are strictly horizontal or strictly
vertical, determined by two diagonally opposite points.

(2) a 4-sided polygon where all 4 sides are strictly horizontal or strictly
vertical, determined by the top-left point ("origin") and bottom-right
point ("corner").

Maybe I'm being unfair, but it seems to me things aren't really clear from
one method to the next what the semantics are supposed to be. I'm getting
the idea that the intent is that the semantics of "rectangle" are supposed
to be (2). Well, OK, as long as we all know that; but in this case,
shouldn't Rectangle origin: (25@250) corner: (100@100) throw an error?

But then that's almost certain to break *somebody's* code ...

It's a puzzle.

Reply | Threaded
Open this post in threaded view
|

Re: Rectangle >> intersects: "degenerate case" -- *wrong* behavior??

Nicolas Cellier
In reply to this post by Nicolas Cellier
I understand how brittle it may seem...
But that's how #intersect: works, when there is no intersection, it
answers a Rectangle with negative width and/or height

So we have not that many options... (unless we go in tons of rewrites
and broken compatibility).
I took above feature as granted, rationalized the implementation
stating that rectangle with negative width/height is empty, and
documented it with SUnit TestCase
That's the minimum change that can possibly work.
I just was a bit too zealous, there is a thin line between empty and
empty, thanks for raising your voice, it's now corrected in trunk :)

If you change #origin:corner: to either answer a correct Rectangle or
raise an Error, you'll break #intersect: and maybe other senders who
knows...
There is a #vertex:vertex: creation message in Visualworks for exactly
that purpose...
In Squeak, it seems we never had the need/idea of it, but you can also
write it ^Rectangle encompassing: {vertex1. vertex2}.

Note that #intersect: #intersects: and #areasOutside: are essential
for Morphic redrawing for example...

Nicolas

2013/1/17 Jim Rosenberg <[hidden email]>:

> --On Tuesday, January 15, 2013 01:49:26 +0100 Nicolas Cellier
> <[hidden email]> wrote:
>
>> Well, the old behavior of intersects: was wrong with respect to intersect:
>>
>> This is because intersect: answer an "empty" rectangle (negative width
>> or height).
>> Such intersection HAS TO BE EMPTY, and thus cannot intersects: anything...
>>
>> To accomodate the degenerated case (one null dimension, the other
>> positive or null) we can change the final tests to strict
>> inequalities. OK?
>
>
> Hmm ...
>
> I guess changing to strict inequality would be OK with *me* -- as far as not
> breaking any of my own code -- but I'm still wondering whether silently
> returning false from Rectangle >> intersects: in the case of "negative
> height" or "negative width"  is the right behavior.
>
> The issue here, as I understand it, is that a "rectangle" where the origin
> and corner are not top-left and bottom-right is "incorrectly specified". So,
> if Rectangle >> intersects: can't really handle an incorrectly specified
> rectangle, should it return false, or throw an error?
>
> Of course changing the behavior to throw an error could break somebody
> else's code, which I wouldn't want to do.
>
> What should Rectangle origin: (25@250) corner: (100@100) produce? A
> rectangle starting from (25@250) and ending at (100@100) certainly makes
> semantic sense. But it's "incorrectly specified". Should it throw an error?
> Should it produce the same thing as
> Rectangle origin: (25@100) corner: (100@250)? Should there be a new class
> method, say Rectangle from: (25@250 to: (100@100) which would be guaranteed
> to produce the rectangle with origin (25@100) and corner (100@250)? Should
> Rectangle >> intersects: "normalize" both the receiver and argument before
> calculating whether things intersect?
>
> What "is" a rectangle? How is the Squeak user supposed to know? There are at
> least two interpretations of what "rectangle" might mean:
>
> (1) a 4-sided polygon where all 4 sides are strictly horizontal or strictly
> vertical, determined by two diagonally opposite points.
>
> (2) a 4-sided polygon where all 4 sides are strictly horizontal or strictly
> vertical, determined by the top-left point ("origin") and bottom-right point
> ("corner").
>
> Maybe I'm being unfair, but it seems to me things aren't really clear from
> one method to the next what the semantics are supposed to be. I'm getting
> the idea that the intent is that the semantics of "rectangle" are supposed
> to be (2). Well, OK, as long as we all know that; but in this case,
> shouldn't Rectangle origin: (25@250) corner: (100@100) throw an error?
>
> But then that's almost certain to break *somebody's* code ...
>
> It's a puzzle.
>