On 5 April 2016 at 16:29, Aliaksei Syrel <alex.sy...@gmail.com> 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? > > Cheers, > Alex > > On Tue, Apr 5, 2016 at 3:15 PM, Aliaksei Syrel <alex.sy...@gmail.com> > 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 <siguc...@gmail.com> 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 <siguc...@gmail.com> wrote: >>> >>>> >>>> >>>> On 5 April 2016 at 04:00, Ben Coman <b...@openinworld.com> wrote: >>>> >>>>> On Tue, Apr 5, 2016 at 2:51 AM, Igor Stasenko <siguc...@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. >>>> >>> >>> >>> >>> -- >>> Best regards, >>> Igor Stasenko. >>> >> >> > -- Best regards, Igor Stasenko.