I started working on Athens by picking up a previous work by Cyrille (if
i'm not mistaken). And his work also predated by Rome plugin , that was
done by (and here my memory fails me)..

Originally it was looked as a simple wrapper of Cairo library, reflecting
its design and API straightly and sharply.
We had a discussion at that time about this and picked a direction.
The reason why we don't want it to be just a Cairo wrapper is obvious. (if
not, ask, i will list them here).
The heaviest influence to Athens design comes from OpenVG standard.
I like it, because it describes all things quite clear and with much detail.


On 6 April 2016 at 11:07, Igor Stasenko <[email protected]> wrote:

>
>
> On 6 April 2016 at 10:35, Nicolai Hess <[email protected]> wrote:
>
>>
>>
>> 2016-04-05 16:31 GMT+02:00 Igor Stasenko <[email protected]>:
>>
>>>
>>>
>>> On 5 April 2016 at 17:27, Igor Stasenko <[email protected]> wrote:
>>>
>>>>
>>>>
>>>> On 5 April 2016 at 16:29, Aliaksei Syrel <[email protected]> wrote:
>>>>
>>>>> Now let's take a look at this code:
>>>>>
>>>>> drawOnSpartaCanvas: aCanvas
>>>>>
>>>>>> aCanvas
>>>>>>   clipPreserveBy: self shape during: [
>>>>>>   aCanvas paintGroup: [
>>>>>> aCanvas setPaint: self shape fillPaint.
>>>>>> aCanvas fillPreserve.
>>>>>> aCanvas paintMode source.
>>>>>> aCanvas setStrokePaint: self shape strokePaint.
>>>>>> aCanvas stroke ] ]
>>>>>
>>>>>
>>>>> You may be curious why it is so ugly :) Make it work - make it right -
>>>>> make it fast. We are on the first etappe, because I invested zero time in
>>>>> rendering stuff.
>>>>>
>>>>> What you see is the minimal amount of cairo primitive calls that are
>>>>> needed to render not overlapping fill and stroke. Clipping is needed to
>>>>> make sure that stroke does not get rendered outside of a path. Group is
>>>>> needed to have transparent target in order to make source paint mode work
>>>>> as expected. Compared to image_surface group, it in this case allows to
>>>>> preserve clip and current cairo state which is pushed to stack during
>>>>> push_group and restored during pop_group_to_source. fillPreserve allows to
>>>>> reuse the same path as used for clipping before saving cpu time on loading
>>>>> path.
>>>>>
>>>>> Yes, i understand that. You are forced to do that. And it is ugly not
>>>> because of all you listed above, it ugly because you could just use a
>>>> cascade:
>>>>
>>>> aCanvas setPaint: self shape fillPaint;
>>>>   fillPreserve;
>>>>    paintMode source;
>>>>    setStrokePaint: self shape strokePaint;
>>>> stroke
>>>>
>>>> (something like that)
>>>> but yeah.. that can wait .. since it is still work in progress. I agree.
>>>>
>>>>
>>>>> It is implemented in canvas specific method after dispatch though
>>>>> canvas, so we are allowed to use canvas specific api, for example groups.
>>>>>
>>>>> How to model stroke, fillPreserve and paintModein terms of Athens?
>>>>>
>>>>>
>>>> A good question. And i don't have an answer to it , ready for you.
>>>> 1. Stroke can be expressed as a special kind of paint. And that how its
>>>> done in Athens.
>>>>
>>>> 2. What fillPreserve is, is can't find it in source code? Some
>>>> hot-swapping and preserving context state , i guess.
>>>>
>>>> 3. As for paint mode, it is already in Athens, so why you asking? You
>>>> don't like how it is done or what?
>>>>
>>>>
>>> or maybe you meant how to group those operation and express them as
>>> command group?
>>> I have no simple answer here. Because this is root points of the core of
>>> graphics engine. From one side, you want such things be exposed to user,
>>> and from other you want a higher dimension concepts/operations to be
>>> allowed by combining those.
>>> There's no simple way. I would just stop at this level, letting user to
>>> decide how he wants to play with those pieces to achieve results he wants.
>>>
>>
>>
>> Grouping and Context save/restore are good additions - I think.
>>
>> But it is true, that we should care about the api of Athens and not just
>> add things that happens to be possible, because we use cairo as a backend.
>>
>> I made some fixes for AthensBalloon (not all are integrated yet, some
>> parts are just experimental and needs more tests), the idea is to have
>> AthensBalloon at least not crash or throwing  errors, even if not all
>> features are supported.
>>
>
> Much, much, much appreciated. The whole point of existence of Balloon
> backend for Athens was to use it as a proving ground that Athens can stay
> backend neutral, and its API allows to stay it like that.
> From that perspective, any feature that offered by any backend should find
> its way via API, but not thrown into play just because we can.
> As i mentioned before, if we would be making Cairo wrapper, then there no
> reason to call it Athens. It could be something like 'CairoPharo'.
>
> And i kept mentioned over and over again on all presentations related to
> Athens, that it is not Cairo.
>
> But at the moment, no one cares about non-cairo-athens. All users just
>> directly use AthensCairoCanvas/AthensCairoSurface.
>> I had a bug report for discussion about how to make some kind of factory
>> that would create the appropriate Athens backend.
>>
>> Is there still some interest on AthensBalloon or to make athens more
>> independent from cairo?
>>
>>
>>
>
> It always been. But didn't have much time to make that happen.
> For instance, i dream to make an OpenGL backend for Athens.. but i had no
> chance to put my hands on that topic so far.
>
>
>>
>>>
>>>
>>>>
>>>>> Cheers,
>>>>> Alex
>>>>>
>>>>> On Tue, Apr 5, 2016 at 3:15 PM, Aliaksei Syrel <[email protected]>
>>>>> wrote:
>>>>>
>>>>>> Hello Igor
>>>>>>
>>>>>> Thanks for extensive design explanation and effort!
>>>>>> Issues you mentioned in previous emails are important and need to be
>>>>>> addressed :)
>>>>>> fill(), stroke() fillPreserve() strokePreserve() need to disappear in
>>>>>> the end. We will come back to them later.
>>>>>>
>>>>>> Let me tell a few words about Sparta.
>>>>>> Sparta implements Athens interface api (excluding some experimental
>>>>>> stuff to test possible performance boost in a few places) and does not 
>>>>>> have
>>>>>> task to remove Athens style and abstractions. Ideally Sparta will be
>>>>>> AthensCairo for bloc. I'm looking forward for your help :)
>>>>>>
>>>>>> Here are some aspects in AthensCairo that Sparta tries to address in
>>>>>> first place:
>>>>>>
>>>>>>    - *Clipping in local coordinates*. It is critical in Bloc. You
>>>>>>    implemented AthensCairo to have vector based rendering in Morphic and 
>>>>>> Pharo
>>>>>>    in general. Morphic lives in global coordinates, so your choice to 
>>>>>> clip in
>>>>>>    global coordinate is perfect! At the same time global clipping in 
>>>>>> bloc adds
>>>>>>    complexity. Sparta clips always in local coordinates (user space in 
>>>>>> cairo
>>>>>>    terminology).
>>>>>>    - *Clip by arbitrary path*. Athens and AthenCairo expect to see
>>>>>>    aRectangle as clipping region - your wise choice for morphic. In bloc 
>>>>>> I
>>>>>>    would have clipping by arbitrary path. clipBy:during: gets aPath.
>>>>>>    Rectangle/Color is polymorphic with path/paint in Sparta
>>>>>>    - *Support of groups*. (maybe user-level aspect? like shadows)
>>>>>>    Groups are powerful in cairo (do they exist outside of cairo?) and 
>>>>>> allow to
>>>>>>    draw both transparent fill and stroke without overlapping using only 
>>>>>> one
>>>>>>    path. On class side of BlElement there are examples (exampleCircle) 
>>>>>> that
>>>>>>    show such behavior.
>>>>>>    - *Do not maintain and set pathTransformation before each
>>>>>>    render-dependent action.* Questionable but what if Canvas will
>>>>>>    not maintain current state of pathTransform? Instead all 
>>>>>> transformations
>>>>>>    can be directly applied on cairo_t using native calls. If there is a 
>>>>>> need
>>>>>>    to get actual matrix we can ask cairo directly. From my perspective it
>>>>>>    simplifies transformation stuff a little bit.
>>>>>>    - *Benefit from cairo_save and cairo_restore.* AthensCairo
>>>>>>    maintains state manually by setting transformation matrix and clip. 
>>>>>> Instead
>>>>>>    we could save and restore state without caring about clip/matrix which
>>>>>>    simplifies code. Check SpartaCanvas>>#clipBy:during:
>>>>>>
>>>>>>
>>>>>> Cheers,
>>>>>> Alex
>>>>>>
>>>>>> On Tue, Apr 5, 2016 at 2:12 PM, Igor Stasenko <[email protected]>
>>>>>> wrote:
>>>>>>
>>>>>>>
>>>>>>> Couple more words about that fill() function abstraction.
>>>>>>> Now you probably understand why there's no notion of stroke
>>>>>>> operation in Athens.
>>>>>>> Because instead of introducing it that way, by adding new kind of a
>>>>>>> function
>>>>>>> stroke(shape,paint)
>>>>>>> from our perspective, it falls into our more generic fill()
>>>>>>> function, except that
>>>>>>> instead of literally filling the shape we deciding to paint a stroke:
>>>>>>> fill(shape, strokePaint).
>>>>>>>
>>>>>>> As i said, there's nothing that tells that fill() function must
>>>>>>> affect only areas enclosed by the shape.
>>>>>>> For instance, you could imagine, that i'm in contrary, may want to
>>>>>>> fill everything , but the area(s) enclosed by given shape. And that 
>>>>>>> still
>>>>>>> can be represented as invocation of our generic fill() function, except
>>>>>>> that we will use a different kind of paint, that will fill everything
>>>>>>> outside designated region, i.e.:
>>>>>>> fill(shape, fillOutsidePaint)
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 5 April 2016 at 14:33, Igor Stasenko <[email protected]> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 5 April 2016 at 04:00, Ben Coman <[email protected]> wrote:
>>>>>>>>
>>>>>>>>> On Tue, Apr 5, 2016 at 2:51 AM, Igor Stasenko <[email protected]>
>>>>>>>>> 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.
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Best regards,
>>>>>>> Igor Stasenko.
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Best regards,
>>>> Igor Stasenko.
>>>>
>>>
>>>
>>>
>>> --
>>> Best regards,
>>> Igor Stasenko.
>>>
>>
>>
>
>
> --
> Best regards,
> Igor Stasenko.
>



-- 
Best regards,
Igor Stasenko.

Reply via email to