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.
