Comment #13 on issue 842 by google....@ben.coman.com.au: ROTranslatingShape mouse hotspot mis-alignment http://code.google.com/p/moose-technology/issues/detail?id=842
Alexandre,
This turned into a bit an essay, but it brings together a number of ideas I've developed in response to trialling ROTranslateShape.
I know Mondrian seemed to have had lots of caches which made the architecture convoluted, however it may be reasonable for a distinction to be made between caching shape data and caching element layout data. (Would I be correct in saying that Mondrian had a lot less concept of shapes?) Elements are greatly affected by nesting and layouts, particularly dynamic layouts - whereas shapes should be at the end of the line, with no sub elements, and within the element-local-coordinates should not change much. Indeed stretching the concept, for "element shapes" (as opposed to "edge shapes") perhaps the drawing can be split into something like a #calculateShape and a #drawShape - where the "convention" is that #calculateShape is only called on "model-changed" and stores ALL PARAMETERS required to draw the shape in instance variables, while #drawShape is called in a tight rendering loop and the "convention" is that it should use NO temporary variables, but only can use only the exact parameters previously stored by #calculateShape. Further, #calculateShape would statically fix the bounds of each shape until the next "model changed" and cache that value into an ROElement ivar perhaps called "localBounds" - so that #containsPoint on needs one check per Element until a hit is found. This would avoid having to process every shape every cycle, which I think is a major part of the slowdown. Only after a hit within an anElement bounds would the shape bounds be double-checked to avoid an invalid hit - like the part where two offset squares don't overlap.
I think the concept of translating shapes is important, but I am actually not sure if I like ROTranslateShape. Apart from the performance aspect, having trialled ROTranslatingShape I think it introduces more complexity than necessary. It also is more awkward and constraining than I would like - for instance if I want to offset four circles N,E,W,S from center, then I need to contend with reversing the cumulation of chained ROTranslatingShapes, rather than just dealing with four absolute offsets from center. With ROTranslatingShape I need to consider how the chained cumulative offset interacts with imposed layering of which shapes get drawn on top.
With the proviso that I haven't much experience in the trenches with graphics programming, I can't help feeling that it would be simpler for each and every element shape to have its own offset. Then I could more easily add offset shapes to an element in the order convenient to the shape layering. I speculate that using such an offset variable would not be much more CPU load than the only other alternative of defining a new shape class for every general combination of offset shapes.
In addition, I've been musing over whether this 'offset' ivar might be a point or a block, or might contain a class something like NullOffset which always returns 0@0; or StaticOffset which always returns a value stored in an instance variable; or StaticOffsetFromBottomOfElement, DynamicOffset which executes a block.stored in an instance variable. DynamicOffset might somehow be tuned into
I have also been musing on whether ChildrenShape should REALLY be the central consideration. Using labels as an example, all the cases I can think of for positioning a label, they are dependent of the size of the ChildrenShape - eg placed under the children shape relies on ChildShape>>height; and placed to the right of the element relies on the ChildrenShape>>width and possibly >>height for central vertically alignment. Following on from this, I think ChildrenShape should always be calculated first, even if it is drawn in a middle layer. ROElement would always hold ROChildrenShape as its first shape, while ROChildrenShape would have two 'next' ivars - one for those shapes above it and one for those shapes below it. The position ROChildrenShape would be "defined" to be _always_ at 0@0 and tied directly to whatever is thought of as the "element" position, with all other shapes offset against it, including in the negative direction for labels appearing above the ChildrenShape.
However if shapes can have a negative offset, then in line with my ideas about caching only the sum of shape bounds in the element, I speculate that the cleanest implementation would be for ROElement>>localBounds to also allow a negative origin, so that it could be the simple union of its ROShape>>bounds.
Some ideas end in dead-ends. A possible path forward is to first revert the changes associated with ROTranslateShape. Then depending on how you view the ideas I presented above, prior to adding any shape translation, get ROElement negative bounds and negative translation working as per Issue 831. Then work individual offsets for shapes, which I believe may be dependent on Issue 831. Anyway, that is just how it appears to me without knowing what other architectural constraints you are working with.
phew! (and whoops, that is a lot for you to digest.) hope some of that is helpful. cheers -ben