[Pharo-dev] [ bloc ] I do not understand why some behavior is not in the right place

Igor Stasenko siguctua at gmail.com
Tue Apr 5 07:33:42 EDT 2016


On 5 April 2016 at 04:00, Ben Coman <btc at openinworld.com> wrote:

> On Tue, Apr 5, 2016 at 2:51 AM, Igor Stasenko <siguctua at gmail.com> wrote:
> >
> > Some more bashing today.. (don't take it personal, i may be wrong)
> >
> > BlPath hierarchy.. and BlShape.
> >
> > Why you redefining what is shape and what is path?
> > Of course, you are free to do it in Bloc..
> > But in terms of Athens, all of BlPath are actually - shapes..
> > And BlShape is some kind of encapsulation of shape, paints and transform.
> > It is a dumb state holder without any extra logic.
> >
> > My rule of thumb: do not produce dumb state holders. They has to be
> smart,
> > else it makes no sense in creating separate entity and designating it as
> > something else than any other bunch of data thrown into single clump,
> > sitting there deaf, blind, dead and silent until someone else will grab
> it
> > somewhere
> > and start using it for own purpose.
> >
> > Sure, i could understand, why you potentially may want such object(s)
> > around,
> > but it is not shape anymore and i wouldn't call it like that. Because
> shape
> > are shape, and has nothing to do with paints and transform,
> > it don't knows and don't cares whether it will be filled or stroked or
> both,
> >  and how many times, and if there will be single paint or thousand.
> > Such kind of properties is simply orthogonal to what shape existing for,
> > because it exists only to define geometry.
> >
> > I think all of that came from not understanding the roles of objects and
> how
> > they interact in Athens.
>
> Can you point us to documentation that describes Athen's architecture
> for these interactions?
> (sorry I haven't checked class comments, but I'm looking to start with
> something at higher level anyway)
>

No, i can't point it out. And you are right , this is nobody else's fault
than my own. I feel ashamed. Sure how i could demand that people understand
the concepts, if i didn't explained then anywhere (or if i did, it is not
in easily reachable place).

So, lets fix that. I will write it down here, and you can pick it up and
find suitable place for it.

----------
Basic abstractions behind Athens.

Since Athens is about drawing graphics, we need a media where all drawing
operations will appear. We call that media a surface.
The surface is abstract. It can have set dimensions, or don't.  We don't
define if it representing some kind of physical surface (like part of the
display screen), or how it storing the data inside. We leaving an
introduction of such details to concrete surface implementation.
All that matters is that surface is a final target of all our drawing
operations.
Therefore, in Athens, a surface is usually a starting point where all
begins from, and you doing so by creating a specific surface.
It is surface's responsibility then, to provide user a means how he can
draw on it, and therefore there is a number of factory methods, that
allowing you to create a canvas, paints and shapes. All those three are
specific implementation of AthensCanvas, AthensPaint and AthensShape
protocols, suitable to be used with specific surface implementation that
you using.

Canvas.
Canvas represents a basic drawing context. We don't allow a direct
operations with surface, but instead we provide a context, that contains
and carries all information that represents a current stage of drawing
operations.
This includes things like, current coordinate transformation(s), currently
selected paint and shape, and paint mode.

In order to obtain canvas, one must use #drawDuring: message sent to
surface with block as argument. The given block receives an instance of
AthensCanvas as a single parameter. We intentionally enclosing all possible
drawing operations within a block to make sure that when we leave, we can
safely release all resources that was allocated, required to hold the
drawing context state. By exposing it in such form, we also making sure
that nothing can alter the surface outside a given block. That way, it
gives users a definitive answer, whether he finished drawing operations or
not, and if it safe to operate with surface for things like saving it to
file, or using it as a source for more complex operations, like acting as a
paint to fill area(s) inside another surface etc.

Paints and shapes.
A starting point is answering a question, how we can represent a simplest,
elementary drawing operation on a surface without putting too much
constraints.
We doing so by postulating that any elementary drawing operation can be
expressed by a function:

fill(paint, shape)

Please, note that 'fill' here is not a literally fill given shape with
given paint. We call it 'fill' for simplicity reason. It can anything that
altering the surface, but always taking into account given parameters:
paint and shape.

Then, from that perspective we can clearly say what are the roles and
responsibility of shapes and paints.

The shape defines the affected region, its geometry and location,
while paint defines how that region will be altered.
In this way, most of more complex operations can be expressed as a series
of such function invocations by using various paints and shapes.

Such representation also gives us a minimal set of roles, a building
bricks, that we need to introduce in order to represent any kind of drawing
operation we may need, as well as a minimal functionality in order to
implement such function(s). And therefore a minimal protocol(s), that all
paints and shapes should implement.

Since there potentially infinite number of various paint kinds and shape
kinds, we cannot make a single function that will implement all possible
permutations in order to fill shape with concrete paint.
To solve that we introducing a straight dispatch mechanism, where we
delegate the responsibility of implementing a concrete case, first to
shape, and then to paint.

The API representing this function in canvas by #draw protocol.
It takes currently selected paint and currently selected shape and starting
dispatch:

draw
"Fill the currently selected shape with currently selected paint"
^ shape paintFillsUsing: paint on: self

So, first it goes to the shape, by sending #paintFillsUsing:on: ,
then shape dispatching it further to paint by sending appropriate message
(be it #athensFillPath:on: or #athensFillRectangle:on: or anything else, if
you want to introduce new kind of shape representation and implement it
accordingly).
Such dispatch gives us an ability to easily extend the framework by
introducing new kind of shapes and paints , by implementing new kind of
fill() functions for them.

-----------

I hope that will make clear at least part of things what is there, behind
the scenes.


> cheers -ben
>
>

-- 
Best regards,
Igor Stasenko.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.pharo.org/pipermail/pharo-dev_lists.pharo.org/attachments/20160405/cf4119c2/attachment.html>


More information about the Pharo-dev mailing list