pyret-lang
pyret-lang copied to clipboard
Overlay after above/beside not working as expected.
If I have this:
include image
STRIPE = rectangle(100, 50, "outline", "black")
CIRCLE = circle(25, "solid", "red")
overlay(CIRCLE, beside(STRIPE, STRIPE))
overlay(beside(STRIPE, STRIPE), CIRCLE)
overlay(CIRCLE, above(STRIPE, STRIPE))
overlay(above(STRIPE, STRIPE), CIRCLE)
The circle is placed on the middle of one of the original rectangles, not on the middle of the new rectangle.
In Racket, this does what I would expect and places the circle in the middle of the joined rectangles.
(require bootstrap2015/bootstrap-teachpack)
(define CIRCLE(circle 25 "solid" "red"))
(define STRIPE(rectangle 100 50 "outline" "black"))
(overlay CIRCLE (beside STRIPE STRIPE))
(overlay (beside STRIPE STRIPE) CIRCLE)
(overlay CIRCLE (above STRIPE STRIPE))
(overlay (above STRIPE STRIPE) CIRCLE)
Are pinholes not reset properly after above and beside?
This was a change we made to preserve the pinholes of the "first" images in beside & above, because otherwise pinhole information is lost by that composition. If you want to recenter the pinhole, you can use center-pinhole to do so.
To be honest, I'm not 100% convinced by this change. I had advocated for it, and we'd converged on it as the new design, but it seems to be more inconvenient than helpful sometimes...
This has tripped me and some of my students up. The exercise was composing a face from smaller parts (eyes, nose, mouth) using overlay. I ended up having them use overlay-align as a workaround to get things to line up, but the intuition of the students (and I agree) is that since you are getting back a new image, the pinhole should be the center of the new image. It seems like the exception, not the common case, where you would want keep track of the first image's pinhole and not have it be the center of the entire image. If it is important to keep it this way, it would be helpful to make a note of this behavior in the documentation.
There is a heuristic I've used in a different implementation of this image library (not in Pyret), where pinholes are preserved only when the images are overlaid on their pinholes in the first place. So, overlay would preserve them, but beside and above would recenter them. I'm torn here too: this heuristic gets most of what I'm concerned for, with regards to making pinholes work better, at the cost of making the behavior even more special-cased.
There are no perfect options here. The behavior we have currently is optimized to make pointy shapes like triangles and stars and carefully overlayed groups all "look right" -- and to do that, the pinhole is not at the center of the image. (E.g. the "center" of an equilateral triangle or a 5-pointed star is not at the midpoint of its bounding box, but rather at the point where if you rotated it, it'd "spin smoothly". Equivalently, if you overlaid it on a circle of the same radius, you'd expect them to line up properly, rather than poke out the top of the circle.) Once that change was made, we needed to change how overlay preserved pinholes, so that you could keep aligning more shapes to that same spot -- if overlay always reset the pinhole to the center, this wouldn't work... And yet I agree, the current behavior has some unintuitive consequences.
The critical take-away, then, is to make sure the documentation says this — and perhaps highlights this, so that it isn't easily lost in a casual reading.
Seems like a design flaw, introduced (explained) here.
If you mix "bounding box-center" and "centroid"... sometime using the one, and then the other... obviously things will not always work out nicely. (Stacking images above/beside each other; does not make sense if you try to force the centroid to have some relevance there)
Perhaps a function centroid-pinhole that works on some basic shapes such as triangles, would have been better (i.e. more consistent)
(I could imagine a 2nd [slightly-modified] image library, that works exactly like racket's image lib; and has an extra function centroid-pinhole.)
Here's current pyret:
overlay(
circle(10, "solid", "green"),
triangle-sss(200, 200, 200, "solid", "blue") # pinhole at centroid
)
->
Here's racket:
(require 2htdp/image)
(overlay
(circle 10 "solid" "green")
(triangle/sss 200 200 200 "solid" "blue") ; pinhole at bounding-box center
)
->
Racket is more consistent and could have something like this:
(require 2htdp/image)
(overlay
(circle 10 "solid" "green")
(centroid-pinhole ; new
(triangle/sss 200 200 200 "solid" "blue"))
)
to move the triangle's pinhole from the bounding-box center, to the centroid.
(But when then stacking images above/beside each other... the new stacked image must have a pinhole that is the center of the bounding-box of the new "joined" image; instead of trying to force a centroid pinhole of a subshape, to have any relevance)
For now: Is it not possible to modify pyret, to have the pinhole of a combined image (combined via above/beside), be at the center of the bounding box of the combined image (as opposed to the current variant of propagating the pinhold of the 1st subimage)???
See here #1748, for why that could be a good option.