While rewriting the node tool I necessarily had to look into path effects. Live path effects are currently seem to be represented by a single svg:path that has information on what the original path looked like in the inkscape:original-d atribute, the applied effects in another attribute, and additional. I think this is not very flexible, because it does not allow for example nondestructive boolean operations where both operands are other path effects. What do you think about this representation:
1. A live path effect is represented by an svg:switch element. An Inkscape-specific attribute can be added to it to simplify implementation, but it is not really necessary. 2. The first element in that svg:switch is an inkscape:path-effect element that contains all the information about objects it was generated from. Shape parameters are represented as inkscape:path-effect-param elements that can contain anything including svg:use, svg:rect, svg:path, another inkscape:path-effect etc. This could allow very nifty things like live boolean operations. It would also simplify the node editor, because it could just operate on the invisible children of inkscape:path-effect while being completely unaware of path effects. As bonus points, path effects created from rectangles, circles, spirals, etc. could preserve the editing capabilities of those shapes. 3. The second element would be an svg:path that contains the result of the path effect, style properties, etc. 4. When opening the file in Inkscape, it would only look at inkscape:path-effect and recompute the fallback svg:path whenever something changes. When opening in another SVG viewer, it would only display the svg:path. 5. To handle live boolean operations, there would be an additional parameter for shapes that says whether they are 'live'. If a shape is 'live', all boolean, offset, etc. operations would create path effects instead of paths, similar to how linked offset works now. In fact, the destructive versions could be implemented as a combination of creating a path effect and an object-to-path conversion. 6. Path parameters would accept any SPShape. There would be a separate type of parameter that accepts a group of shapes. To convince an effect to accept an object group as a parameter, a special effect called Flatten Group would be provided - it would convert a group into a path effect suitable for use as a parameter for another one, and could optionally use a clone rather than the original for flattening.
Now some use cases: 1. To create an outline of some collection of objects, we create clones of them, group them, apply Flatten Group and then Offset. Result: whenever any of those objects is moved or modified, the outline updates. 2. The sun and cloud example in http://wiki.inkscape.org/wiki/index.php/Lpe-blueprint would be dead simple: create the circle and the sun ray, use pattern along path; create a clone of the cloud, make it 'live', create an offset; then apply a live subtraction between the sun and the clone. Updating any of the original shapes will now change the appearance. (As a related note, compensation on moving should not be a global property, but a property of the clone! As demonstrated here, we want the invisible clone used for the path effect to move with the cloud, but we want other visible clones to stay where they are when the original is moved)
UI changes: The LPE stack would need to be changed into an LPE tree like the layer dialog. The path effect tool would allow the editing of various parameters using control points and the node editor; selecting an item in the tree would cause its editing controls and toolbar to appear. Live path effects would be created from a menu rather than from the tool, like dynamic offset.
Internal changes: SPLPEItem would be removed, and SPShape would be used as the type of object that can be used as a path parameter.
What do you think? I think when done this way, path effects (or maybe they should be called Vector Effects, because they're not really limited to paths?) provide a generic vector equivalent to filtering. This means they could become super-powerful, and in fact the main method of editing complex drawings. There would be some problems with legacy live path effects, but it can be dealt with at document load.
PS I just noticed this is somewhat similar to the vector effects proposal in SVG 1.2. It might be wise to plan ahead and make it easy to optionally store some path effects as an SVG 1.2 vector effect.
Regards, Krzysztof
2009/8/2 Krzysztof Kosiński <tweenk.pl@...400...>:
While rewriting the node tool I necessarily had to look into path effects. Live path effects are currently seem to be represented by a single svg:path that has information on what the original path looked like in the inkscape:original-d atribute, the applied effects in another attribute, and additional. I think this is not very flexible, because it does not allow for example nondestructive boolean operations where both operands are other path effects.
Actually it does, and we even have these boolean operations in the codebase. They are just disabled for now because they are buggy on the 2geom side. But they work and they are live :)
- A live path effect is represented by an svg:switch element.
That would work, and it is indeed more generic in that it would allow other replacements than path->path, for example path->bitmap or anything else. In fact, svg:switch is just a general mechanism for these kinds of renderer-specific stuff. But the intent of "path effects" is path->path mapping - its entire terminology and tools are focused on this. Other mappings should be treated separately, and indeed they would use svg:switch. For example, we URGENTLY need to convert our flowtext into a switch which would have our flowtext stuff in the Inkscape branch and its exact equivalent using svg:text in the default branch.
However, for the path->path domain of path effects, our current representation without switch is adequate, especially since it was made applicable to groups and thus made possible live booleans. I just don't see what a change you're proposing would buy us.
- The first element in that svg:switch is an inkscape:path-effect element
that contains all the information about objects it was generated from. Shape parameters are represented as inkscape:path-effect-param elements that can contain anything including svg:use, svg:rect, svg:path, another inkscape:path-effect etc. This could allow very nifty things like live boolean operations. It would also simplify the node editor, because it could just operate on the invisible children of inkscape:path-effect while being completely unaware of path effects.
But it will still need to know to go into your switch's correct branch to find them. Just as now, it just knows to read the inkscape:original-d attribute instead of d, but otherwise it is "completely unaware of path effects."
As bonus points, path effects created from rectangles, circles, spirals, etc. could preserve the editing capabilities of those shapes.
And they do now. Try assigning Gears to an ellipse - you will still see ellipse handles that are editable, and the effect will update live. There are three problems with this right now:
- it only works in shape tools, not in Node - hopefully your node tool rewrite will take care of that
- if you assign an effect which has its own knotholder, such as Hatches, you will lose ellipse handles because we can only have one knotholder at a time. Again, this is either in the scope, or very close to it, of your SoC work, so hopefully we'll see this fixed soon :)
- rect does not use path element at all and so cannot have any path effects - this needs to be fixed in SPRect, has been planned for a long time.
So, these three issues need to be addressed, but this does not require us to change the SVG representation at all.
Now some use cases:
- To create an outline of some collection of objects, we create clones of
them, group them, apply Flatten Group and then Offset. Result: whenever any of those objects is moved or modified, the outline updates.
This is possible now, if only we had these effects available. One of them is there but disabled - it's Union. Offset badly needs to be written (hey 2geom folks!) so we can finally remove the "offset object" abomination.
What do you think? I think when done this way, path effects (or maybe they should be called Vector Effects, because they're not really limited to paths?) provide a generic vector equivalent to filtering. This means they could become super-powerful, and in fact the main method of editing complex drawings. There would be some problems with legacy live path effects, but it can be dealt with at document load.
Well, I think our path effects are already a "vector equivalent to filtering" and "super-powerful" :) They just need to be cleaned up, UI limitations fixed (which you are working on right now!) and then it will be dream come true :)
bulia byak wrote:
But it will still need to know to go into your switch's correct branch to find them. Just as now, it just knows to read the inkscape:original-d attribute instead of d, but otherwise it is "completely unaware of path effects."
If it knows to read a different attribute, then it obviously is aware of path effects. Using the switch structure would entirely remove the need to care about path effects, so that things work automatically. By using this new XML structure we reduce a great deal of complexity. The node editor is no longer "something that can edit some element that has some attribute that has SVG path data". It becomes "something that can edit an svg:path", and nothing more.
Here is an example how it could look like in the document: svg:switch <inkscape:path-effect type="boolean-union"> <svg:path ... /> <inkscape:path-effect type="boolean-difference"> <svg:use ... /> <svg:use ... /> </inkscape:path-effect> </inkscape:path-effect> <svg:path ...result... /> </svg:switch>
- it only works in shape tools, not in Node - hopefully your node tool
rewrite will take care of that
I can say with confidence that the new structure would allow my node tool to work with path parameters without introducing even 1 line of code related to LPEs. That's one of the reasons I suggested it. The node tool is very complex and it is important to keep it as simple as possible, so that bugs can be found and people other that me can understand it.
- if you assign an effect which has its own knotholder, such as
Hatches, you will lose ellipse handles because we can only have one knotholder at a time.
The proposed XML representation would allow one to actually select the parameter objects without the tools knowing they are path effect parameters - this problem would go away instantly. It would make path effects a different type of object, so they can have their own event contexts to edit them with control points and the like, without overloading elements that were not designed to edit them. We could edit the ellipse or the offset without ANY hacks either in the offset editor or in the ellipse tool - it would just be the normal behavior of switching the tool in response to selecting an object.
- rect does not use path element at all and so cannot have any path
effects - this needs to be fixed in SPRect, has been planned for a long time.
I do not agree with the plan to turn svg:rect into an svg:path. Using svg:path to represent a rectangle removes even more semantic information from the document and makes life harder for whoever uses our markup. Instead of trying to make everything an svg:path (the whole point of SPShape is to avoid this!) we should allow effects to accept more types of shapes as parameters. Preserving semantic information is the whole point of this new XML structure, and in fact it is in large part motivated by the svg:rect and svg:ellipse problems. It would allow *arbitrary* SVG elements to be used as input for path effects, because the SPObject layer would automatically create the shape information for all parameters of the path effect whatever they are.
This is possible now, if only we had these effects available. One of them is there but disabled - it's Union.
Flatten Group is not Union. Union performs a boolean 'or' of all shapes whereas flatten is more or less equivalent to the 'combine' operation. On top of that, I'm not sure whether the boolean operations are able to also take the second parameter "by reference" so that the result is updated whenever any of the arguments changes. The new XML is perfectly capable of that because it can use a clone.
Well, I think our path effects are already a "vector equivalent to filtering" and "super-powerful" :) They just need to be cleaned up, UI limitations fixed (which you are working on right now!) and then it will be dream come true :)
I agree they are already useful despite their limitations, though maybe not 'super-powerful'. I just think that the simplest way to achieve this 'cleanup and fixing' without introducing LPE-related functions to every tool is to change the XML representation. If we don't do it, we'll end up either with a ton of unnecessary junk in every tool, or adding a third layer between the XML and SPObject. If we change the XML, we can leverage a lot of existing code to get an impressive feature set, and even simplify it a lot. In other words, I think it's the quickest way to achieve the desired functionality. I cannot find a way in which the existing implementation is superior to the proposed one, except the fact that the existing one is partially done (though with several bugs, limitations, and far reaching interdependencies) and the new one is not.
Regards, Krzysztof
Hi, Your proposition sounds good to me, at least worth thinking about it twice!!
I'm not familiar enough with internal inkscape code (node tool, shape tools, helper path display, etc...) to be definitely sure it simplifies everything so well, but I agree it looks so.
The main issue I can think of is that we already released lpes with the actual xml representation, and maintaining compatibility might be painfull. But this can be understood in the other direction: should we change something to the xml structure then the earlier the better. And in all cases, think about it twice!!
Let me start with the main reason I think some move in that direction should be taken:
The path to path mapping paradigm absolutely needs one exception: lpe need to be allowed for clones (clones can be used as parameters, but we need to allow them as ground object). This is absolutely required, event for most path to path mapping observing lpes: suppose you want to draw several outlines of the same shape, or to skectch the same path with several stroke styles, widths, or sketch it and fill with hatches, etc... there are tons of situations where different lpes have to be applied "in parallel", i.e. to clones of the same path to get the desired effect. Atm, you can only use copies, which means editing the ground path is forbidden (making "live effects" not so "live" anymore). Our xml representation does not allow clones, and this is a strong limitation. Of course we can work around it using more fancy "inkscape:blah" parameters to remember the object was a clone and store it's original href, but I'm pretty sure this will mess up the code and introduce terribly complicated tests everywhere. We definitely need a representation that allows more general paths as input: anything that can be converted to a path should be allowed. I think your proposition is good for that.
[ Now some remarks about less convincing points:
About parameters: we can already use complex objects (in particular, objects carrying lpes on theire own) as parameters using "cloned" parameters. The only limitation is that the source object has to exist somewhere in your drawing and, you generally have to hide it by hand in a separate layer or something... I think this is not a big problem. Recieving a group as parameter should not be a big deal, but since we can only output a single path, this is not very usefull, is it?
About bool ops, the actual structure is to have one "ground object" and get the other operands as parameters. I think this is ok. Your proposition makes the things more symetrical, replacing the ground object by a (sorted) list of operands. This is closer to the user workflow, who may want to select a collection of objects, and click somewhere to get the union or instersection of all at once. I doubt this is a big trouble however; since we can still make an arbitrary choice of the ground object and add as many bool ops as needed. (I didn't get your proposition about making a shape 'live').
one new feature your xml representation allows is to have a separate new object as output: this allows in particular the style to be edited (set opacity according to area, or whatever), and more generally lpes to output groups: this is required to get the interpolate extension for instance, where not only the shape is interpolated but also the style. There are tons of situations where this is desired. However, this violates the path to path mapping paradigm, and hence goes beyond lpe purpose. In particular, this requires a completely new API. This means we need a more general "vector effect" object, which after all is nothing more than an lpe allowed to output groups... We all know we'll need this sooner or later. Once we have it, we'll can think about letting lpes be particular cases of vector effects. ]
Finally, if this new lpe generation should look like filters, it might be worth organizing the inputs as "slots", like filters do: one lpe in the stack could use the result of another as a parameter(?). Hm. This is maybe too complex for only a few use cases...
Anyway, I support your proposition; I've be looking for something like that for a long time, but didn't know the switch element, and did never end up with a clean solution. Thank's!
Cheers, jfb.
2009/8/2 jf barraud <jf.barraud@...400...>:
The path to path mapping paradigm absolutely needs one exception: lpe need to be allowed for clones (clones can be used as parameters, but we need to allow them as ground object). This is absolutely required, event for most path to path mapping observing lpes: suppose you want to draw several outlines of the same shape, or to skectch the same path with several stroke styles, widths, or sketch it and fill with hatches, etc... there are tons of situations where different lpes have to be applied "in parallel", i.e. to clones of the same path to get the desired effect.
This is absolutely doable with the current representation. We just need to add a trivial "copy" LPE which will link to some path and create a copy of it, just like the pattern-along-path now links to the pattern path. Once you get such a copy, you can stack other LPEs on it, and the copy will be live just like with clones (or like with linked offset, which also must be migrated to a LPE). We already have link LPE parameter and some UI for it, so it just needs to be coded. No need to change anything in XML representation for it.
Hi, atm, I agree it can be done with the actual representation, I even used to do it using the boolops lpe, with the "identity B" operation (!) (althought I had troubles with transforms). But this is somewhat strange, since the "clone" lpe would be applied to... no path. It sounds like a hack. Does the node tool agree working with empty paths? Should a default meaningless path be provided??...
Moreover, I feel we will meet the "transform" issue again. I did already insist too much on that so I won't take it again, but I'm still not convinced we handle transforms correctly. It's another issue anyway (talso concerning the xml representation however...).
Cheers, jfb.
2009/8/3 bulia byak <buliabyak@...400...>
This is absolutely doable with the current representation. We just need to add a trivial "copy" LPE which will link to some path and create a copy of it, just like the pattern-along-path now links to the pattern path. Once you get such a copy, you can stack other LPEs on it, and the copy will be live just like with clones (or like with linked offset, which also must be migrated to a LPE). We already have link LPE parameter and some UI for it, so it just needs to be coded. No need to change anything in XML representation for it.
jf barraud wrote:
The main issue I can think of is that we already released lpes with the actual xml representation, and maintaining compatibility might be painfull.
It can be taken care of in the document loading code (along XML parsing) or even implemented as an XSLT that is applied on every entry document, so that the SPObject layer never sees the old representations.
About parameters: we can already use complex objects (in particular, objects carrying lpes on theire own) as parameters using "cloned" parameters. The only limitation is that the source object has to exist somewhere in your drawing and, you generally have to hide it by hand in a separate layer or something... I think this is not a big problem. Recieving a group as parameter should not be a big deal, but since we can only output a single path, this is not very usefull, is it?
I only mentioned groups because they are used in some current effects. They would also be useful with clones (e.g. when you want to apply an effect to a group that has both clones and shapes).
About bool ops, the actual structure is to have one "ground object" and get the other operands as parameters. I think this is ok.
This introduces additional complexity when you want to edit one of the objects: the path manipulator or the shape manipulator must know that it will be editing a parameter of a path effect, not an actual shape (because the XML representation is different). In the proposed representation, the manipulators can just edit actual XML / SPObject elements that are identical to the elements they edit "normally", and you can select those elements without any hacks - they have an XML representation and a node in the XML tree so they interoperate with Inkscape::Selection without any changes.
I doubt this is a big trouble however; since we can still make an arbitrary choice of the ground object and add as many bool ops as needed. (I didn't get your proposition about making a shape 'live').
The bit about 'live' shapes is just an UI metaphor. It is a way of specifying whether the boolean operation should replace the shapes with a new path, or create a path effect while preserving the original shapes. If the selection contains a live shape, the operation creates a path effect, otherwise it creates a normal path. Note that this is compatible with how users work: you usually know which shapes in the drawing are 'important' and should not be destroyed by path operations, so you can make them "live" to avoid inadvertently destroying their editing capabilities.
However, this violates the path to path mapping paradigm, and hence goes beyond lpe purpose. In particular, this requires a completely new API.
That's the thing - the paradigm itself is limited! Thinking of an LPE as a path->path mapping is inherently limited, and can only accomodate effects taking multiple paths as input using hacks. When we think of an LPE as a shape dynamically computed from other shapes, the new representation is very natural. (Note that I do not use the word 'path' - this also includes ellipses, rectangles, etc.) I don't deny that significant changes are needed, but I think in the long run it will be worth it.
Finally, if this new lpe generation should look like filters, it might be worth organizing the inputs as "slots", like filters do: one lpe in the stack could use the result of another as a parameter(?).
This is exactly what I've demonstrated in the example and requires no special handling in the new representation: since an LPE is a shape, and LPEs accept shapes as parameters, an LPE can accept another LPE as a parameter, or even a clone of it. You can create a tree of any depth and still have all the operands selectable (from the LPE dialog) and editable.
bulia byak wrote:
This is absolutely doable with the current representation. We just need to add a trivial "copy" LPE which will link to some path and create a copy of it, just like the pattern-along-path now links to the pattern path.
With enough hacks one could probably achieve all the things I'm talking about even with the old representation. I am not talking about what is doable but what is simple and maintainable. Look at Windows 95: it was possible to do a 32-bit multitasking GUI on top of a 16-bit single-tasking DOS, but the code must have been a total abomination as evidenced by its instability and bugs.
Not really. The node tool will still have to know from which branch to take shapes (e.g. when you click or press Ctrl+A) and which branch to ignore.
This only seems like a problem now because our handling of the switch element is currently slightly wrong, and we need to fix it anyway to legalize flowtext. The SPObject layer should not know anything about switches. SP tree is a semantic representation of the document, so it should only contain the path effect representation, and the fallback path representation should not be accessible from the SP tree. In other words, the fallback path should never be selectable, and its SPObject should not exist at all. It makes no sense to edit that path in the UI anyway! If you want to do this, use Object to Path. Selecting a path effect should not edit its parameters, but the effect itself. Editing the parameters should be possible from the LPE dialog in a manner similar to an object browser.
It's not really that simple. Consider LPEs which refer to other paths, for example Pattern along path. These paths can be anywhere, on canvas or in defs.
It is possible to select and edit a path that is in defs, because it has an SPPath object. It's just not selectable by clicking on the canvas. I already do this in my node tool when I edit clips and masks (I have already implemented and it and will send it in the next patch btw, once I finish constrained dragging).
This means it will have to know how to traverse from a path with such path parameter to that path parameter, read it, and display it.
In my node tool I edit a single path with a PathManipulator. The manipulators do not need to traverse anything - they are dumb objects that take an SPShape and display the controls to edit it. It is the event context (tool) that takes care of figuring out which items need to be edited and creating manipulators for them. The LPE dialog would be responsible for creating the manipulators for parameters.
And it should use at least a different color for the parameter path, so it must be aware that it is a different kind of path.
The color of the path will be a property of the manipulator, not something the manipulator figures out on its own. The tool would be responsible for setting it. The LPE dialog would set the correct color when displaying a manipulator for its parameter, because it knows they are parameters.
Which means, you will end up with basically what we have now. I don't see where you can shave off much complexity here.
That's because you think of the node tool as a single entity, while I think of it as a wrapper for path manipulators. The node tool handles selecting objects, switching to clip editing mode, etc., while manipulators are completely oblivious of all this and edit what they are told to. This is where the complexity can be lowered.
This looks nifty, but I just don't see how this is principally different from LPE on a group that we can do now. Is it only that the implementation can be simpler with your structure?
Yes, it can be MUCH simpler, because the manipulators can be created for the internal XML elements. I can create a path manipulator for the svg:path inside the inkscape:path-effect element. Groups, rectangles, polylines, other path effects and clones do not need any special handling, because the SPObject representing the path effect treats them as SPShapes without caring about what XML elements they are actually represented with. This allows LPEs for svg:polyline, svg:ellipse, etc. without converting them to paths.
I can tell you one thing: the implementation of the current LPE seemed really simple when we were just planning it :) Most of the complexity grew upon it later - when we ran into various scenarios and thought about new possibilities. That is perfectly normal, and I don't think our LPE implementation is particularly bad as it is. But I'm pretty sure that no matter how sleek your implementation seems now in theory, in practice it will be much much hairier :)
It's not pure theory because I'm already using a similar approach when editing clipping paths that are in defs. I needed about 30 lines of code to add this feature, and half of it was related to testing whether there is something to edit at all. I just get a list of paths in the clip/mask and create manipulators for them - that's all! I did not have to modify even a single line of code in the PathManipulator. It will be really simple to do things with the new representation, for the same reason editing clipping paths is simple: they have XML and SPObject representations identical to 'normal' objects.
BTW, the clones here will not be editable by Node tool anyway, as they aren't now.
Of course they won't, the original will be editable. I thought it was obvious. When the original is edited, the LPE will update, because the clone will change. It is wrong to come up with yet another mechanism of linking to path parameters rather than copying them if we can reuse the one provided by SVG.
Your syntax would allow that too, of course, but it would mean nested switches which adds considerable complexity: the non-Inkscape branch of such a nested switch will still be inside an Inkscape branch of the parent switch! It will be cumbersome to decide what to render and what to edit with such a structure. With our current implementation, it's all in a single path element and therefore much simpler and more straightforward.
In my initial proposal there are no nested switches. Look at the example again: there is only one switch that surrounds the topmost LPE only. However, using nested switches is actually be a better idea, because it allows us to degrade gracefully when we don't understand one of the nested path effects, and would make the XML representation of the LPEs look the same regardless of whether they are topmost or not. Handling the switch does not add extra complexity to the tools - it only needs to be handled differently in the XML->SPObject code, so that an SPPathEffect object is created for the svg:switch XML node, and the SPSwitch object or the SPPath object for the fallback is not created. By the way, having SPSwitch at all feels wrong: the SPObject layer should abstract this away.
Yes, but again, it is desirable that pattern paths use a different outline color and maybe some other presentation or behavior differences (for example it would be nice to edit the pattern not where it is but moved towards the skeleton that uses it), so the node tool will have to learn all this stuff anyway.
Under the new internal implementation, they won't have to - the LPE dialog will take care of setting the appearance properties of manipulators. I will also add an externally settable outline transform to the path manipulator, so that the outline can be transformed with respect to the path manipulator. The LPE dialog / tool will know about those transforms and will set them, but the manipulators will not need to learn anything new beyond exposing a few extra parameters for external use. The complexity of being able to set a different outline color is extremely trivial when compared to that of being able to edit something that has a different XML representation.
Also, your XML example shows only boolean effects where source paths play more or less the same role. What about pattern along path, where one path is skeleton and the other is pattern? How do you identify in your structure which is which?
The same way z-order is determined, by their order in the document :) If that turns out to cause problems, I'll add an inkscape:parameter-name attribute to that says which parameter it is, but right now I don't see a case where it would be necessary.
Why "every tool"? All we need to do is: teach the Node tool to edit parameter paths (done), teach the knotholder to edit knots of an LPE (done), and make sure all these knotholders and multiple paths are editable simultaneously (is being worked on).
That last part is actually the most complex one. The first two are trivial by comparison. I can say this from experience :) Even if we have to scrap the first two parts to make the last one simpler, it will be worth it; actually the first two parts are not even necessary with the new XML representation. And why we need to edit them all at once? The new representation allows you to select and edit one parameter at a time or several of them at once.
By the way, I agree that "all tools" is a bit scary, but when I think of it not all tools will have to be modified, only the shape tools. The shape tools are simple when compared to the Node tool, so it won't take very long, and on top of that I will reuse many components I wrote for the new node tool. I could probably rewrite the Ellipse tool in a week or two at most.
Whereas with your implementation, I foresee changes at least in Selector - it will need to be taught how to select stuff inside switches and what to ignore there.
The selector is not supposed to select anything inside those switches. Selector is not the only way to select objects - for example you can select paths for editing in my node tool even tough it does not use any selector code. Any changes would be confined to sp_event_context_find_item, which is the function used to find items to select.
All export and rendering code will all have to be updated as well, otherwise it will just export both source paths and the result path on top of one another. That's quite some rewriting to do, for no clear gain that I can see.
The rendering code does not touch the XML layer, it uses the SPObject layer, so it won't need any changes. Some export plugins might need to be fixed to handle switches correctly, but we would need to do this anyway for the flowtext work. For me the gain is enormous. The new representation is generic - if we use some object as a path effect parameter, it does not change at all, and is in every respect still a normal object! The only difference is that it's not rendered on the canvas. We can exploit this to make things brilliantly simple.
With the old representation we have know that we are operating on an LPE almost everywhere. I don't say it is incapable of providing all the features we want - probably it could provide them, but only after we made a huge mess with a lot of obscure bugs and quirks and alienated everyone out of working with tool code, because every non-trivial change would break LPEs in some subtle way.
Regards, Krzysztof
Hello,
I have tried to use the current LPE implementation to add connection points to a shape for my GSoC project improving the connector tool. I was considering LPEs because there was already implemented: - parameter values handling; - knot editing via node tool; - LPE stacking; - not interfering with other tools.
So it looked that it would be appropriate to implement connection points (they are sort of knots) by means of LPE without having to "hack" sp-item for this. Still I soon discovered some drawbacks that made me reconsider taking this approach: - LPEs turn everything into paths: that would seem odd to the user that an ellipse is no more editable only because it has connection points; - LPEs are what they say: path effects, so they modify the path in certain ways. On the other side connection points are more an addition to an item, not modifying it in a visible way, but more in a logical way (adds new qualities/features to the items). - You can't add/delete knots yet (in the code I saw comments that suggested it will be done at some point) - I guess that at the moment of design decisions one didn't envision effects that would need this feature.
Yet I saw an example of an effect that does not modify the path (text label) - which in fact gave me some hopes at the begining because it also looked like a "logical" (or should I say virtual) effect, which only added a feature to the path, but did not modify it.
From what I could understand Krzysztof has implemented these PathManipulators,
which seem to extend (am I wrong?) the ShapeEditor or the KnotHolders so as to provide means for every kind of LPE to define its own ways to edit the paths, so I could exploit this for connection points I think.
Since the talk arrived to a potential LPE re-design, could you consider also these "virtual" LPE, which are not in fact path effects, but more path features or qualities?
For example, that text-label effect (which is currently in testing) is not a real path effect, but the author thought it would be nice to show that this can be done with what is currently implemented.
Still there are things that cannot be done with current LPEs: for example having a label (svg:text) on a shape as an LPE would turn that shape into a path (which is wrong), but this could be a usable feature (having labels on shapes that could adapt rotation, horizontal/vertical flip in a proper way). I think I could imagine other uses for these "item features" and I was wondering if Krzysztof's proposal could also handle these.
Regards, Arcadie
Arcadie M. Cracan wrote:
From what I could understand Krzysztof has implemented these
PathManipulators,
which seem to extend (am I wrong?) the ShapeEditor or the KnotHolders so as to provide means for every kind of LPE to define its own ways to edit the paths, so I could exploit this for connection points I think.
PathManipulator does not use ShapeEditor or Knotholder. I will write a new object that will more or less replace ShapeEditor (it will store a list of shape manipulators and allow to act on them in a generic way), and Knotholder will go away entirely and be replaced with a separate object for each shape: EllipseManipulator, RectangleManipulator, PolygonManipulator, etc. There will also be a function that allows you to create a manipulator from an SPItem that will edit it; it will eventually be made into a virtual method, so you will be able to write 'spobject->getManipulator()' to create a manipulator for an SPObject without knowing its type (but it could return NULL if the object doesn't support manipulation, like a clone).
Since the talk arrived to a potential LPE re-design, could you consider also these "virtual" LPE, which are not in fact path effects, but more path features or qualities?
It can be done with the new representation. Either the path effect could make its parameters normally visible and selectable, or it could implicitly create 'unmovable' clones (ones that are always at the same place as the source object - as I said before, in addition to the default clone compensation setting it should be possible to set the compensation mode per clone). I'm leaning toward the second idea because if we consider 3 items where each 2 are joined by a connector, it becomes obvious that the 'connector' path effect cannot take ownership of the objects. Another way to implement connectors is to create a different switch like this: svg:switch <inkscape:connector from="#object171" to="#object223" /> <svg:path ...(the appearance of the connector)... /> </svg:switch> The new XML representation would turn path effects into ordinary objects, and since it relies heavily on switch handling it would benefit all other things that would rely on switches in future, like flowtext, the above connector example, and many more.
Still there are things that cannot be done with current LPEs: for example having a label (svg:text) on a shape as an LPE would turn that shape into a path (which is wrong), but this could be a usable feature (having labels on shapes that could adapt rotation, horizontal/vertical flip in a proper way).
This is another place where the new representation turns out superior, and I didn't think of it before: the result of the effect is not limited to a path. For the label effect, instead of converting to path, we would just output the actual objects used: for example, for a rectangle with a text label, we would output svg:g that contains svg:rect and svg:tspan. The switch could contain an inkscape:object-label element in the first branch and a group of the two objects (the labelled one and the label) in the fallback fork.
This opens up tons of possibilities: 1. Editable tilings where you can drag control points to adjust the spacing and tiling area - something like the clone tiler dialog but does not cause you bodily harm. (Currently you have to tweak all parameters by putting numbers in entry fields.) 2. Text effects that output actual text rather than paths, for example an effect that scatters the letters. 3. More types of gradients! (we put a clipped svg:image or clipped and blurred circles in the result fork, while in the renderer we directly compute the gradient from its parameters)
Now that I think of it, we could use different element names for path effects, so that creating the SP nodes does not require looking at the type attribute: inkscape:boolean-union, inkscape:boolean-difference, inkscape:boolean-subtraction, inkscape:pattern-along-path, inkscape:render-gears, inkscape:bend-path, inkscape:offset, etc.
I can try to prototype this for dynamic offset after GSoC. I just noticed my node tool breaks it: using the node tool was the only way to bring up its knotholder, and my tool doesn't (yet) know how to edit things other than paths. The new offset would use the aforementioned switch structure, and the linked offset would be removed (it would be possible to create an offset of a clone which would work exactly like linked offset does now); at the same time the capability to edit the original shape would be preserved.
Regards, Krzysztof
On Tuesday 04 August 2009 03:16:36 am Krzysztof Kosiński wrote:
This is another place where the new representation turns out superior, and I didn't think of it before: the result of the effect is not limited to a path. For the label effect, instead of converting to path, we would just output the actual objects used: for example, for a rectangle with a text label, we would output svg:g that contains svg:rect and svg:tspan. The switch could contain an inkscape:object-label element in the first branch and a group of the two objects (the labelled one and the label) in the fallback fork.
This opens up tons of possibilities:
- Editable tilings where you can drag control points to adjust the spacing
and tiling area - something like the clone tiler dialog but does not cause you bodily harm. (Currently you have to tweak all parameters by putting numbers in entry fields.) 2. Text effects that output actual text rather than paths, for example an effect that scatters the letters.
Great! So there is place for a LPE that turns for example LaTeX formatted text into (possibly gtkmathview) rendered math text on screen. This would be a very nice addition to Inkscape!
- More types of gradients! (we put a clipped svg:image or clipped and
blurred circles in the result fork, while in the renderer we directly compute the gradient from its parameters)
Now that I think of it, we could use different element names for path effects, so that creating the SP nodes does not require looking at the type attribute: inkscape:boolean-union, inkscape:boolean-difference, inkscape:boolean-subtraction, inkscape:pattern-along-path, inkscape:render-gears, inkscape:bend-path, inkscape:offset, etc.
I can try to prototype this for dynamic offset after GSoC. I just noticed my node tool breaks it: using the node tool was the only way to bring up its knotholder, and my tool doesn't (yet) know how to edit things other than paths. The new offset would use the aforementioned switch structure, and the linked offset would be removed (it would be possible to create an offset of a clone which would work exactly like linked offset does now); at the same time the capability to edit the original shape would be preserved.
Regards, Krzysztof
Regards, Arcadie
Arcadie M. Cracan wrote:
Great! So there is place for a LPE that turns for example LaTeX formatted text into (possibly gtkmathview) rendered math text on screen. This would be a very nice addition to Inkscape!
With this structure it is even possible to call true LaTeX and process its output into SVG text, but this requires support for SVGFonts (the LaTeX fonts generally aren't available to the native font system, so we'd have to use either SVGFonts or paths).
Regards, Krzysztof Kosiński
2009/8/3 Krzysztof Kosiński <tweenk.pl@...400...>:
About bool ops, the actual structure is to have one "ground object" and get the other operands as parameters. I think this is ok.
This introduces additional complexity when you want to edit one of the objects: the path manipulator or the shape manipulator must know that it will be editing a parameter of a path effect, not an actual shape (because the XML representation is different). In the proposed representation, the manipulators can just edit actual XML / SPObject elements that are identical to the elements they edit "normally", and you can select those elements without any hacks - they have an XML representation and a node in the XML tree so they interoperate with Inkscape::Selection without any changes.
The greatest problem being, they should not be entirely "normally" edited. They need to be presented as something different than the main path, otherwise it will be very confusing for the user ("what are these ghost paths? can I just delete them?")
However, this violates the path to path mapping paradigm, and hence goes beyond lpe purpose. In particular, this requires a completely new API.
That's the thing - the paradigm itself is limited! Thinking of an LPE as a path->path mapping is inherently limited, and can only accomodate effects taking multiple paths as input using hacks.
Why LPE on a group is a hack? It is simple and valid SVG, and it works.
Again: the path-to-path limitation is indeed a limitation, but that's by design. These are PATH effects, and the entire API is oriented this way. For different things, invent different concepts.
When we think of an LPE as a shape dynamically computed from other shapes, the new representation is very natural. (Note that I do not use the word 'path' - this also includes ellipses, rectangles, etc.)
If all you do with your proposal is extend source objects from paths to paths plus shapes, then you don't need to bother - as I said, we already support LPE on shapes (those that use svg:path, at least, which means all except rect for now).
With enough hacks one could probably achieve all the things I'm talking about even with the old representation.
I think this is true for both representations :)
This means it will have to know how to traverse from a path with such path parameter to that path parameter, read it, and display it.
In my node tool I edit a single path with a PathManipulator. The manipulators do not need to traverse anything - they are dumb objects that take an SPShape and display the controls to edit it. It is the event context (tool) that takes care of figuring out which items need to be edited and creating manipulators for them. The LPE dialog would be responsible for creating the manipulators for parameters.
No no no. Node tool must NOT depend on any dialog for any of its functionality. This breaks encapsulation in a bad way. The dialog may be on or off, this should not affect the tool. The tool must know itself what to do for LPE parameter paths.
And it should use at least a different color for the parameter path, so it must be aware that it is a different kind of path.
The color of the path will be a property of the manipulator, not something the manipulator figures out on its own. The tool would be responsible for setting it. The LPE dialog would set the correct color when displaying a manipulator for its parameter, because it knows they are parameters.
Right now I can edit parameter paths without going to any dialog, and this functionality must be preserved.
Which means, you will end up with basically what we have now. I don't see where you can shave off much complexity here.
That's because you think of the node tool as a single entity, while I think of it as a wrapper for path manipulators.
No I don't. I'm not against the idea of "wrapper for path manipulators" of course - my own ShapeEditor idea was exactly that. But, regardless of which level it is, Node tool (not some dialog!) must know how to deal with LPEs, as I explained above.
This looks nifty, but I just don't see how this is principally different from LPE on a group that we can do now. Is it only that the implementation can be simpler with your structure?
Yes, it can be MUCH simpler, because the manipulators can be created for the internal XML elements.
Just like the current nodepath can also be fed a path in defs, and edit it just fine :)
In my initial proposal there are no nested switches. Look at the example again: there is only one switch that surrounds the topmost LPE only. However, using nested switches is actually be a better idea, because it allows us to degrade gracefully when we don't understand one of the nested path effects, and would make the XML representation of the LPEs look the same regardless of whether they are topmost or not.
OK, can you please write out a detailed XML for your representation of this:
- Two paths, A and B, with different style (one red, the other green).
- A has Bend applied, linked to some path C on canvas as its bend path.
- B has Pattern along path applied, linked to some path D as its pattern path.
- A and B are grouped and the group has Envelope applied, linked to the same C as its bottom envelope path. Note that in the result, A and B still have different style.
I can do this with current implementation, and I can edit all aspects of it. Please show me how it will look in your representation and in what ways it will be easier to implement or edit.
Also, your XML example shows only boolean effects where source paths play more or less the same role. What about pattern along path, where one path is skeleton and the other is pattern? How do you identify in your structure which is which?
The same way z-order is determined, by their order in the document :) If that turns out to cause problems, I'll add an inkscape:parameter-name attribute to that says which parameter it is, but right now I don't see a case where it would be necessary.
It's wrong to consider only implementation forgetting about the readability of the XML source. If you have two sibling paths and they play very different roles, it must be clear from the XML code what is what.
Why "every tool"? All we need to do is: teach the Node tool to edit parameter paths (done), teach the knotholder to edit knots of an LPE (done), and make sure all these knotholders and multiple paths are editable simultaneously (is being worked on).
That last part is actually the most complex one. The first two are trivial by comparison. I can say this from experience :) Even if we have to scrap the first two parts to make the last one simpler, it will be worth it; actually the first two parts are not even necessary with the new XML representation.
So, are you saying that the old representation makes it more difficult for you to achieve the multiple-paths-and-knotholders-at-the-same-time goal? In what way? Please describe some specific problems and maybe we can figure it out together.
And why we need to edit them all at once? The new representation allows you to select and edit one parameter at a time or several of them at once.
I don't see how old representation would not allow you to do the same.
The rendering code does not touch the XML layer, it uses the SPObject layer, so it won't need any changes. Some export plugins might need to be fixed to handle switches correctly, but we would need to do this anyway for the flowtext work. For me the gain is enormous. The new representation is generic - if we use some object as a path effect parameter, it does not change at all, and is in every respect still a normal object!
How is this different? Link to a path making it a pattern for some other path, and it does not change at all, is still a normal object, and editing it updates the result path of the LPE.
With the old representation we have know that we are operating on an LPE almost everywhere.
No, only in places that edit it - which are: nodepath, knotholder, and the LPE dialog. That's all.
I don't say it is incapable of providing all the features we want - probably it could provide them, but only after we made a huge mess with a lot of obscure bugs and quirks and alienated everyone out of working with tool code, because every non-trivial change would break LPEs in some subtle way.
I don't see this at all.
I have discussed and participated in LPE development from the start. We had many difficulties along the way - transforms, stacking, groups, linking and sharing linked paths, and many others. Most of them would be the same with your representation. But I don't remember that we ever had much trouble with enabling Node tool to edit all this stuff. I think this was done quite quickly. Of course it was clumsy because of the one-path-at-a-time and one-knotholder-at-a-time limitation, but that's just the fundamental limitation of the tool, which you are solving now, and has nothing to do with LPEs.
I know it will be very easy to plug in LPE editing into your new node tool. But believe me, it wasn't much harder with the old tool. Just like you (probably) do, we also had an upper layer which chose which attribute to read and what color to use for it, and the lower-level guts which were totally agnostic about what kind of path they are dealing with. Of course it wasn't as cleanly separated as in your new code, but it was separated, and worked fine. If you're trying to simplify something in this area, I think it's not a very fertile one for simplification :)
One point where I totally agree with you is that we do need better support for switches. Whether we use them for LPE or not, we will certainly use them at least for flowtext and probably eventually for some other custom stuff (such as TeX objects), so we must deal with it cleanly, and if you rework this along the lines you described, this will be extremely helpful.
bulia byak wrote:
The greatest problem being, they should not be entirely "normally" edited. They need to be presented as something different than the main path, otherwise it will be very confusing for the user ("what are these ghost paths? can I just delete them?")
That doesn't require any LPE-specific code in the manipulators, just a function to set the outline color and some logic in whatever examines the selection and creates the manipulators.
Why LPE on a group is a hack? It is simple and valid SVG, and it works.
Again: the path-to-path limitation is indeed a limitation, but that's by design. These are PATH effects, and the entire API is oriented this way. For different things, invent different concepts.
You just answered yourself: the current repr would be satisfactory if LPEs were a strict path-to-path mapping, but they are not. For example, Bend is not a path-to-path mapping, it is two-paths-to-path mapping. A group is not a path, so LPE on a group doesn't fit the path-to-path paradigm either. I would have no problems with the current representation if it was really intended to be used for pure path effects, but it is not.
If all you do with your proposal is extend source objects from paths to paths plus shapes, then you don't need to bother - as I said, we already support LPE on shapes (those that use svg:path, at least, which means all except rect for now).
The old repr requires that we turn all input shapes into paths and preserve editing capability using extra attributes, so the manipulators need to know how to edit both those representations; in the new one they only need to know how to edit the correct semantic representation (svg:rect or svg:ellipse).
No no no. Node tool must NOT depend on any dialog for any of its functionality. This breaks encapsulation in a bad way. The dialog may be on or off, this should not affect the tool. The tool must know itself what to do for LPE parameter paths.
The node tool does not depend on this dialog, it's just a way to select invisible objects.
Yes, it can be MUCH simpler, because the manipulators can be created for the internal XML elements.
Just like the current nodepath can also be fed a path in defs, and edit it just fine :)
That's right - it's proven that editing invisible objects that have normal XML elements is simple, so why not take advantage of this for path effects as well, rather than inventing clever ways to preserve editability?
OK, can you please write out a detailed XML for your representation of this:
Two paths, A and B, with different style (one red, the other green).
A has Bend applied, linked to some path C on canvas as its bend path.
B has Pattern along path applied, linked to some path D as its pattern
path.
- A and B are grouped and the group has Envelope applied, linked to
the same C as its bottom envelope path. Note that in the result, A and B still have different style.
I can do this with current implementation, and I can edit all aspects of it. Please show me how it will look in your representation and in what ways it will be easier to implement or edit.
If I understood the description correctly, it would look like this (with nested switches):
svg:switch inkscape:envelope-deformation <svg:g inkscape:param-name="object"> svg:switch inkscape:pattern-along-path <svg:path inkscape:param-name="path" id="pathA" ... /> <svg:use inkscape:param-name="pattern" href="url(pathD)" ... /> </inkscape:pattern-along-path> <svg:path ...(result of pattern along path)... /> </svg:switch> svg:switch inkscape:bend <svg:path inkscape:param-name="path" id="pathB" ... /> <svg:use inkscape:param-name="bend-path" href="url(#pathC)" ... /> </inkscape:bend> <svg:path ...(result of bend)... /> </svg:switch> </svg:g> <svg:use inkscape:param-name="envelope-bottom" href="url(#pathC)" ... /> </inkscape:envelope-deformation> <svg:path ...(result of envelope)... /> </svg:switch>
Somewhere else in the document: <svg:path id="pathC" ... /> <svg:path id="pathD" ... />
In the new repr every parameter can be given either 'by value' (by using it directly) or 'by reference' (by using its clone). By the way, if some other version of Inkscape does not understand e.g. inkscape:bend, then it could use its result path and only the bend part would not be editable, instead of the entire effect. Right now it seems that even if only one effect in the stack is not recognized, the entire stack is not editable.
It's wrong to consider only implementation forgetting about the readability of the XML source. If you have two sibling paths and they play very different roles, it must be clear from the XML code what is what.
An attribute like inkscape:param-name can be used, which I added to the example above.
So, are you saying that the old representation makes it more difficult for you to achieve the multiple-paths-and-knotholders-at-the-same-time goal? In what way? Please describe some specific problems and maybe we can figure it out together.
Having multiple knotholders isn't a big problem, but to select only some of them we need a different concept of selection, because some of the shapes to edit share an XML node (for example, if I stack two effects on a path, the a single XML element represents 3 editable objects: two path effects and the original path. In the new repr we just put the items to edit in Inkscape::Selection.
I don't say it is incapable of providing all the features we want - probably it could provide them, but only after we made a huge mess with a lot of obscure bugs and quirks and alienated everyone out of working with tool code, because every non-trivial change would break LPEs in some subtle way.
I don't see this at all.
OK, maybe that was an overstatement. But if we have to edit two different representations of a rectangle, then something is wrong, especially since the second one (path + attributes) is mostly useless outside of LPEs.
PS: Another possible change that gives similar benefits and probably needs less changes is if we replace inkscape:original-d with a hyperreference to the original shape, and just move that shape to defs instead of moving its data to a special attribute. That's how SVG 1.2 vector effects work - all shape parameters are given using a href.
Regards, Krzysztof
2009/8/6 Krzysztof Kosiński <tweenk.pl@...400...>:
Why LPE on a group is a hack? It is simple and valid SVG, and it works.
Again: the path-to-path limitation is indeed a limitation, but that's by design. These are PATH effects, and the entire API is oriented this way. For different things, invent different concepts.
You just answered yourself: the current repr would be satisfactory if LPEs were a strict path-to-path mapping, but they are not. For example, Bend is not a path-to-path mapping, it is two-paths-to-path mapping. A group is not a path, so LPE on a group doesn't fit the path-to-path paradigm either. I would have no problems with the current representation if it was really intended to be used for pure path effects, but it is not.
OK, I stand corrected: our LPEs are not path-to-path. They are more like anything-to-path. That anything can be a single path, multiple paths with different roles, links to paths, or any other data. For example, it is easy to imagine a LPE for variable-width strokes where the widths are recorded as an array, stored in the path effect in an attribute. The output path will then depend on this data. Yes, we will need a new parameter type and a new attribute for each new kind of such data, but adding new parameter types is relatively straightforward thanks to Johan's code. And you would need exactly the same - a new class and a new attribute for a new kind of source data - with your representation. So I don't see any gain here.
If all you do with your proposal is extend source objects from paths to paths plus shapes, then you don't need to bother - as I said, we already support LPE on shapes (those that use svg:path, at least, which means all except rect for now).
The old repr requires that we turn all input shapes into paths and preserve editing capability using extra attributes,
You mean, requires that we turn the only remaining one, rect, to using paths _when necessary_. This is not such a bad thing at all, because it gives us other advantages beyong LPEs, see (*) below.
so the manipulators need to know how to edit both those representations; in the new one they only need to know how to edit the correct semantic representation (svg:rect or svg:ellipse).
Yes, and once we have implemented and tested code for different-XML-depending-on-features on rect, we can reuse the same approach for ellipses too, and make them use svg:ellipse when this is possible and svg:path otherwise.
Yes, it can be MUCH simpler, because the manipulators can be created for the internal XML elements.
Just like the current nodepath can also be fed a path in defs, and edit it just fine :)
That's right - it's proven that editing invisible objects that have normal XML elements is simple, so why not take advantage of this for path effects as well, rather than inventing clever ways to preserve editability?
There's nothing especially clever about that - we just tell it to read and write to a different attribute (possibly of a different element) instead of d on svg:path. It's no big deal really. I don't remember the details but I think most of deciding what to use is done by the LPEItem itself, not by node tool - the node tool just asks the LPEItem for its parameter paths if any and uses what it gets in return.
svg:switch inkscape:envelope-deformation <svg:g inkscape:param-name="object"> svg:switch inkscape:pattern-along-path <svg:path inkscape:param-name="path" id="pathA" ... /> <svg:use inkscape:param-name="pattern" href="url(pathD)" ... /> </inkscape:pattern-along-path> <svg:path ...(result of pattern along path)... /> </svg:switch> svg:switch inkscape:bend <svg:path inkscape:param-name="path" id="pathB" ... /> <svg:use inkscape:param-name="bend-path" href="url(#pathC)" ... /> </inkscape:bend> <svg:path ...(result of bend)... /> </svg:switch> </svg:g> <svg:use inkscape:param-name="envelope-bottom" href="url(#pathC)" ... /> </inkscape:envelope-deformation> <svg:path ...(result of envelope)... /> </svg:switch>
1. The single result path does not allow different styles on the two paths, as in my original description. You will have a single-colored path while I have one red and one green, both under the same envelope. If you just add a second result path to the top-level switch, it will be extremely inelegant because it will not show the correspondence between the source paths in one branch and result paths in the other. That red transforms to red and green to green will then be an "implied knowledge" - something we should avoid. The flow of information in the document should be obvious from its structure and nothing else.
2. svg:use has its cost. Inkscape clones have a lot of code for behavior when the original is transformed or deleted. I guess most of this will not apply here where the use is only for linking to a path, so it will have to be special-cased in many places. Not nice.
3. Using inkscape-specific container elements like inkscape:envelope-deformation which contain normal SVG elements is at least risky. So far we only had extension stuff in attributes or in terminal elements. The only exception is flowtext (again, because we considered it standard SVG when it was implemented) which can contain shapes for the text flow as descendants - and it's not pretty. There's a lot of special-casing for this in hundreds of tree traversals we have all around the code, and some problems with transforms are still not resolved. What you propose amounts to adding more such stuff, one new Inkscape element type per LPE, which will be hardly maintainable. Also this violates the XML philosophy which says, "ignore anything you don't understand." If you have container elements in Inkscape namespace, other renderers will have to _descend_ into these elements instead of ignoring them completely. Yes, they are only present in the Inkscape branch of a switch, but it's a violation nevertheless.
4. Nested switches may not be wrong but they are at least weird, similar to:
if (x) { if (x) { ... } else ... } else ...
They are also inefficient, not because the same condition is evaluated multiple times (that is OK), but because the internal else is never reached no matter what. Neither Inkscape nor other renders will ever see the result paths in your nested switches. Yet, you will spend resources of generating and updating those elements, and I can tell you that XML tree updates and especially path attribute generation and parsing are very slow operations. The current implementation avoids this - path data is passed inside a stack of LPEs without serialization.
In the new repr every parameter can be given either 'by value' (by using it directly) or 'by reference' (by using its clone). By the way, if some other version of Inkscape does not understand e.g. inkscape:bend, then it could use its result path and only the bend part would not be editable, instead of the entire effect. Right now it seems that even if only one effect in the stack is not recognized, the entire stack is not editable.
Yes, this is indeed an advantage of your approach - due to the inefficiency of serializing the intermediate stages that I just described, you can recover partial stacks where some effects are broken or unknown. However I'm not sure it's such a big advantage: I don't think people would want to edit the remaining LPEs in such a broken stack. They would either upgrade Inkscape if they want to edit the entire stack properly (btw we can record the version-minimum attribute for each LPE and give a friendly suggestion), or they would just deal with the result path as a regular path without any LPEs, as the current Inkscape treats it.
Having multiple knotholders isn't a big problem, but to select only some of them we need a different concept of selection, because some of the shapes to edit share an XML node (for example, if I stack two effects on a path, the a single XML element represents 3 editable objects: two path effects and the original path.
Just as we do when a path has a clippath and a mask. If we can do this for clips/masks, why not do the same for LPE parameters?
I don't understand your point about selecting knotholders - they are not selectable and don't need to be selected. Just display all relevant knotholders for selected object and let the user deal with them using tooltips, knot colors, etc.
In the new repr we just put the items to edit in Inkscape::Selection.
You can't use desktop's selection for that - only user can change it. If I selected an object (which in your repr is a top-level switch), it must stay selected. Selection must not sink into its depths "on its own" to select some stuff inside. We have so much stuff hung upon selection-changed callbacks that it will be a messup of gigantic proportions :)
And if you just create your own Inkscape::Selection for the purpose, this is fine, but I just don't see how this is much simpler than the current approach where node tool just asks the LPE system what to edit and gets a complete list of paths that we soon (thanks to you) will be able to edit all simultaneously.
OK, maybe that was an overstatement. But if we have to edit two different representations of a rectangle, then something is wrong, especially since the second one (path + attributes) is mostly useless outside of LPEs.
No it's not useless at all. You forget about two more things: markers and text on path. With current SVG standard, you cannot have those on a rect element, but users want to use them with rects all the time. So, even without any LPEs, we have more than enough reasons to switch rects to svg:path. We would be quite justified to do this even unconditionally, without any svg:rect fallback; if we implement the conditional rect/path SVG backend it's only because we try to play nice with fans of semantic SVG :)
PS: Another possible change that gives similar benefits and probably needs less changes is if we replace inkscape:original-d with a hyperreference to the original shape, and just move that shape to defs instead of moving its data to a special attribute. That's how SVG 1.2 vector effects work - all shape parameters are given using a href.
Yes, this is possible too, but I don't see why duplicate the style, id, and the rest of it when I only need one attribute?
Hi,
I'll leave discussion about code simplification aside, because I know the evil hides in the details, and I have a far too vague idea of the internal code to make up my mind (and I doubt arguing about virtual code complexity at this level will lead to any decisive point).
My opinion is the following: the main point with the thing->path paradigm is that you cannot edit the style of the output. This is by desing, and I'm happy this choice was made, and I think it was a smart choice: we now have our good working and simple to implement lpes! On the other hand, we will sooner or later want to have LOEs (live-"object"-effects) to allow more sophisticated effect (like interpolate shape+style between two objects).
I think we should keep lpes as they are and implement the more sophisticated LOEs. Once they work correctly, we'll can move the lpes to be special cases of LOEs.
Doing the move Kzysztof suggests seem to be more or less equivalent to implementing LOE (am I wrong there?)... The main point is to allow other things than paths (virtually any independent well formed svg object) as output which is the main benefit I see in this representation. Moreover it looks deep and difficult, so there is no hurry anyway. So what about starting to work on LOE's instead of changing LPE's?
Btw, I think the good output for the example of bulia would be:
svg:switch inkscape:envelope-deformation <svg:g inkscape:param-name="object"> svg:switch inkscape:pattern-along-path <svg:path inkscape:param-name="path" id="pathA" ... /> <svg:use inkscape:param-name="pattern" href="url(pathD)" ... /> </inkscape:pattern-along-path> <svg:path ...(result of pattern along path)... /> </svg:switch> svg:switch inkscape:bend <svg:path inkscape:param-name="path" id="pathB" ... /> <svg:use inkscape:param-name="bend-path" href="url(#pathC)" ... /> </inkscape:bend> <svg:path ...(result of bend)... /> </svg:switch> </svg:g> <svg:use inkscape:param-name="envelope-bottom" href="url(#pathC)" ... /> </inkscape:envelope-deformation> * svg:g < ...(result of envelope for A)... /> < ...(result of envelope for B)... /> </svg:g> * </svg:switch>
Of course, the API to produce such output is much more complex!! The lpe itself should be aware it is working on a group and put the relevant outputs in a group... (of course the default behaviour would be coded once for all...)
cheers, jfb.
2009/8/7 jf barraud <jf.barraud@...400...>:
My opinion is the following: the main point with the thing->path paradigm is that you cannot edit the style of the output.
That is, you cannot edit it with the LPE - the result simply always has the style of the source path. This is simple and automatic with current representation, because it is just the same element. With proposed representation, it will require extra coding to properly copy the style (which is not trivial, because a style may be a result of a complex layering of parent styles), though of course it will also open new possibilities.
This is by desing, and I'm happy this choice was made, and I think it was a smart choice: we now have our good working and simple to implement lpes!
That's exactly my point.
On the other hand, we will sooner or later want to have LOEs (live-"object"-effects) to allow more sophisticated effect (like interpolate shape+style between two objects).
I think we should keep lpes as they are and implement the more sophisticated LOEs. Once they work correctly, we'll can move the lpes to be special cases of LOEs.
Agreed, and I think this could be a compromise that we all can agree upon. Let's not jump the gun. If Krzysztof or anyone implements the switch-based infrastructure and proves it workable with at least one non-LPE object (such as TeX objects or something like that), then we can consider switching LPEs to that mechanism. Until then, let's not fix what isn't broken.
Btw, I think the good output for the example of bulia would be:
Your example fixes the problem with styles, but it still has the other issues I pointed out - inefficiency of nested switches, SVG elements inside non-SVG, and potential problems with svg:use.
bulia byak wrote:
OK, I stand corrected: our LPEs are not path-to-path. They are more like anything-to-path. That anything can be a single path, multiple paths with different roles, links to paths, or any other data. For example, it is easy to imagine a LPE for variable-width strokes where the widths are recorded as an array, stored in the path effect in an attribute. The output path will then depend on this data. Yes, we will need a new parameter type and a new attribute for each new kind of such data, but adding new parameter types is relatively straightforward thanks to Johan's code.
The only problem I have with this is that the "ground path" is not a real object. If it was preserved in the defs (even stripped of style, etc.) rather than in a special attribute, it would be nicer. Other than this, I agree with JFB that it might be best to leave path effects alone until the more powerful replacement (vector effects) is created.
- svg:use has its cost. Inkscape clones have a lot of code for
behavior when the original is transformed or deleted.
That there is lots of code for this stems from weak href management in our code, for which I have a solution which I will post today or tommorow. Basically objects have hrefcounts and 1-way references where in fact we need 3-way references. With good href management, most such code could be simplified into one or two recursive loops. Same goes for clipboard code.
What you propose amounts to adding more such stuff, one new Inkscape element type per LPE, which will be hardly maintainable.
Why? Most of the code (I'd say at least 80% of the hard stuff) could be shared between all LPEs, because we can use a common base for their SP tree objects that would provide most of the functionality needed by LPEs. Having one SPObject per LPE is a must anyway.
Also this violates the XML philosophy which says, "ignore anything you don't understand." If you have container elements in Inkscape namespace, other renderers will have to _descend_ into these elements instead of ignoring them completely. Yes, they are only present in the Inkscape branch of a switch, but it's a violation nevertheless.
Are you trying to say that if I put a standard SVG element inside an Inkscape-specific element, then an SVG-conforming parser will try to render the inner SVG element? No, this should never happen. After reading the relevant bits of the spec, that is definitely not the behavior I would expect from any conforming implementation. SVG user agents are only required to include the unknown elements in the DOM (if applicable), but should ignore them when rendering. For me this means that the renderer should not look inside the unknown element for anything SVG-like, because doing this is not ignoring, it is actively trying to interpret an unknown element. As long as we do not have XML syntax errors, nothing wrong will happen, unless the viewer is totally broken.
Just as we do when a path has a clippath and a mask. If we can do this for clips/masks, why not do the same for LPE parameters?
There is a difference: the LPE ground path is not an object, while all other path parameters are. If this minor nonorthogonality was fixed then it would be OK.
I don't understand your point about selecting knotholders - they are not selectable and don't need to be selected.
No, it's not about selecting knotholders, it's about reusing the existing Inkscape::Selection as a list of objects for which to display manipulators. If some path parameters are not objects, then it's harder to use the selection to determine what should be edited and what shouldn't. What I had in mind is to have an object tree in place of the current LPE stack, and the user would select the invisible shape parameters to edit by selecting them using that dialog. If the entire LPE was selected, then based on the preferences only the LPE's controls would be shown, or manipulators for everything would be shown; while when selecting some internal objects in the LPE, only the selected objects would have their manipulators shown. It's very similar to how selecting in groups work. Even when some objects are grouped, you can select inside the group. I want to reuse this fact for LPEs.
PS: Another possible change that gives similar benefits and probably needs less changes is if we replace inkscape:original-d with a hyperreference to the original shape, and just move that shape to defs instead of moving its data to a special attribute. That's how SVG 1.2 vector effects work - all shape parameters are given using a href.
Yes, this is possible too, but I don't see why duplicate the style, id, and the rest of it when I only need one attribute?
The style can be stripped when the path is moved to defs. Duplication is not a problem in that solution, but there might be some problems with transforms. I think this solution can be realistically applied to the existing path effects (the original one is better to implement as something completely separate). We only need to copy the original path (with its style stripped) to defs, instead of moving its data to another attribute. This would allow for really symmetric boolean operations, and could probably simplify the code a bit, because there would be no fundamental difference between the source path and other path parameters.
Regards, Krzysztof
2009/8/10 Krzysztof Kosiński <tweenk.pl@...400...>:
The only problem I have with this is that the "ground path" is not a real object.
What is "real"? Why d= is real but original-d= is not? I think it's pure terminology issue. The code is the same.
- svg:use has its cost. Inkscape clones have a lot of code for
behavior when the original is transformed or deleted.
That there is lots of code for this stems from weak href management in our code, for which I have a solution which I will post today or tommorow.
No, our href management is adequate for clones. We have all necessary signals and callbacks. I was referring to the code that compensates transforms of clones so that they behave logically from the user viewpoint when the original is transformed. This is purely behavior code and will get in the way if you use clones for some "helper" or "invisible" paths. Again, we were already bitten by this once with flowed text.
What you propose amounts to adding more such stuff, one new Inkscape element type per LPE, which will be hardly maintainable.
Why? Most of the code (I'd say at least 80% of the hard stuff) could be shared between all LPEs, because we can use a common base for their SP tree objects that would provide most of the functionality needed by LPEs. Having one SPObject per LPE is a must anyway.
Once again: our code has hundreds of tree traversals. They all work the same for groups and layers, but they often have to special-case flowtext because it is special and has special rules. This is bad. Once you add a non-SVG container element, you have to define a lot of semantics for it: how style is inherited to/from it, how transforms are handled below it, etc. etc. This is all worked out for svg:g and is used, often implicitly, in many many places. So this will be a mess. Now that I think some more about it, I think we should make this a hard rule: no non-SVG container elements for SVG elements. If you need a container, subclass svg:g as we did for layers.
Are you trying to say that if I put a standard SVG element inside an Inkscape-specific element, then an SVG-conforming parser will try to render the inner SVG element?
It might. No guarantees here. The only guarantee is svg:switch, which you also have at the upper layer, but it looks wrong below it. Even if it works fine :) For example, by thus hiding SVG inside non-SVG, you are also hiding it from SVG validators.
Just as we do when a path has a clippath and a mask. If we can do this for clips/masks, why not do the same for LPE parameters?
There is a difference: the LPE ground path is not an object,
Why do you need an object so badly? What you wrote is called Path Manipulator, not Object Manipulator :) And "path" is just a sequence of path commands, which can well be stored in an attribute of any element.
I don't understand your point about selecting knotholders - they are not selectable and don't need to be selected.
No, it's not about selecting knotholders, it's about reusing the existing Inkscape::Selection as a list of objects for which to display manipulators. If some path parameters are not objects, then it's harder to use the selection to determine what should be edited and what shouldn't.
The point of Inkscape::Selection is the signals it emits. Do you need those signals? I think not, in which case any GSList or other container would work just as fine.
What I had in mind is to have an object tree in place of the current LPE stack, and the user would select the invisible shape parameters to edit by selecting them using that dialog. If the entire LPE was selected, then based on the preferences only the LPE's controls would be shown, or manipulators for everything would be shown; while when selecting some internal objects in the LPE, only the selected objects would have their manipulators shown. It's very similar to how selecting in groups work. Even when some objects are grouped, you can select inside the group. I want to reuse this fact for LPEs.
So what's the problem with our current LPEs which are applicable to groups? And you can select inside those groups, of course. Not "like in groups" with some switches and Inkscape non-g elements treated "as" groups, like in your variant, but simply and literally inside groups with LPEs on them. For example by Ctrl+click. Isn't it even simpler?
At least, it is for things like boolean ops or envelopes. For LPEs where one path is a e.g. pattern upon another, it's not a group in our system. But it won't be in yours either! You won't be able to select the pattern by Ctrl+click because it is invisible by itself. The only way to select inside such a "group" would be by using some list or tree in the dialog. But so you can do with the current system, too. What's the advantage?
bulia byak wrote:
What is "real"? Why d= is real but original-d= is not? I think it's pure terminology issue. The code is the same.
The svg:path that represents the LPE is both a representation of the result of the LPE, and the original path. By a "real object" I roughly mean an object that can be legitimately put in a href. The original-d attribute cannot, so it is not a "real object" in some sense.
No, our href management is adequate for clones. We have all necessary signals and callbacks. I was referring to the code that compensates transforms of clones so that they behave logically from the user viewpoint when the original is transformed.
This code might be useful rather than getting in the way. If create a 'linked' effect usng a clone of some object, I would expect the clone used as the parameter not to move according to the rules for clones. If the behavior is consistent between clones and 'linked' parameters of LPEs, it's a good thing. Moreover the svg:use parameters of LPEs would be created from clones, so it's natural to expect them to behave like clones. As an example, consider linked offset: its behavior is really weird to me, because it does not behave the same way a clone would.
As for href management: good href management would allow us to transform clones during dragging (right now they are only transformed when the original's transform finishes - not good) and do other interesting things.
Once again: our code has hundreds of tree traversals. They all work the same for groups and layers, but they often have to special-case flowtext because it is special and has special rules.
This shouldn't be more complicated than 'if (SP_IS_VECTOR_EFFECT(item)) continue;', or not adding the children of the effect to SPObject 'children' attribute and instead providing access to them using a different function - if it's more involved then it highlights a larger problem.
Once you add a non-SVG container element, you have to define a lot of semantics for it: how style is inherited to/from it, how transforms are handled below it, etc. etc. This is all worked out for svg:g and is used, often implicitly, in many many places. So this will be a mess. Now that I think some more about it, I think we should make this a hard rule: no non-SVG container elements for SVG elements. If you need a container, subclass svg:g as we did for layers.
A non-SVG element can act exactly like a group, if its SP node is derived from SPGroup. If svg:g can act as the 'generic case' of handling transforms, styles, etc. for container elements, then I don't understand why we cannot have SVG elements in non-SVG ones.
Are you trying to say that if I put a standard SVG element inside an Inkscape-specific element, then an SVG-conforming parser will try to render the inner SVG element?
It might. No guarantees here.
I believe there is actually a hard guarantee. The only instance where this might happen that I imagine is when an implementation tries to be actively non-conformant, and descends into unknown elements. Descending into an element is not ignoring it, and implementations are required to ignore unknown elements, therefore SVG implementations are required not to display SVG elements that might reside inside unknown elements. Ultimately, it would be most productive to conduct some tests with actual SVG implementations.
The 'move path to defs and make a href' approach is not affected by this issue.
The only guarantee is svg:switch, which you also have at the upper layer, but it looks wrong below it. Even if it works fine :) For example, by thus hiding SVG inside non-SVG, you are also hiding it from SVG validators.
SVG validation is a special case of XML validation. If the XML validator has DTDs for all namespaces in the document, then it should be able to validate even the elements inside non-SVG elements, provided that they have DTDs that specify that SVG elements can be present there.
The point of Inkscape::Selection is the signals it emits. Do you need those signals? I think not, in which case any GSList or other container would work just as fine.
I need the equivalent of selection_changed to know when to create or destroy manipulators :)
You won't be able to select the pattern by Ctrl+click because it is invisible by itself. The only way to select inside such a "group" would be by using some list or tree in the dialog. But so you can do with the current system, too. What's the advantage?
I assume that we're talking about the second idea (change original-d into a href to a path in defs). I would be able to use the selection_changed callback of Inkscape::Selection to create or destroy the manipulators for all path parameters, including the source path. Otherwise I'd have two options: a) use some kind of sub-object selection, so that the user can select whether he wants to edit the LPE itself, the source path, or both b) always show the source path manipulator when the LPE is selected With option b) it wouldn't be possible to only show the knots of the LPE, or only the source path; with complex LPEs or source paths this might be a problem.
Regards, Krzysztof
I just came back from vacation and see this long thread about LPEs. I think I'm with Bulia on all points (but perhaps I missed something).
Before changing anything, I advice to thoroughly look through the current LPE code and understand it all. I think it is safe to say that nobody knows all about LPE anymore (in any case, its original author lost track of all the intricate details quite a while ago). All the different desired LPEs and use cases make for a pretty large set of details that have to be sorted out. I like the representation that we have now: it is human readable, concise and not overly verbose, and very closely follows the LPE design we had in mind.
(btw, have a look at SVG vector effects (svg1.2 i think), if you want more flexibility)
Cheers, Johan
2009/8/2 Krzysztof Kosiński <tweenk.pl@...400...>:
But it will still need to know to go into your switch's correct branch to find them. Just as now, it just knows to read the inkscape:original-d attribute instead of d, but otherwise it is "completely unaware of path effects."
If it knows to read a different attribute, then it obviously is aware of path effects. Using the switch structure would entirely remove the need to care about path effects, so that things work automatically.
Not really. The node tool will still have to know from which branch to take shapes (e.g. when you click or press Ctrl+A) and which branch to ignore. I just don't see how this is better than knowing which attribute to take and which to ignore.
By using this new XML structure we reduce a great deal of complexity. The node editor is no longer "something that can edit some element that has some attribute that has SVG path data". It becomes "something that can edit an svg:path", and nothing more.
It's not really that simple. Consider LPEs which refer to other paths, for example Pattern along path. These paths can be anywhere, on canvas or in defs. I want Node tool to be able to edit both the skeleton path and the pattern path. This means it will have to know how to traverse from a path with such path parameter to that path parameter, read it, and display it. And it should use at least a different color for the parameter path, so it must be aware that it is a different kind of path. Which means, you will end up with basically what we have now. I don't see where you can shave off much complexity here. Node tool will in any case be central to Inkscape, and it will have to be aware of a lot of things around it.
Here is an example how it could look like in the document: svg:switch <inkscape:path-effect type="boolean-union"> <svg:path ... /> <inkscape:path-effect type="boolean-difference"> <svg:use ... /> <svg:use ... /> </inkscape:path-effect> </inkscape:path-effect> <svg:path ...result... /> </svg:switch>
This looks nifty, but I just don't see how this is principally different from LPE on a group that we can do now. Is it only that the implementation can be simpler with your structure?
I can tell you one thing: the implementation of the current LPE seemed really simple when we were just planning it :) Most of the complexity grew upon it later - when we ran into various scenarios and thought about new possibilities. That is perfectly normal, and I don't think our LPE implementation is particularly bad as it is. But I'm pretty sure that no matter how sleek your implementation seems now in theory, in practice it will be much much hairier :)
BTW, the clones here will not be editable by Node tool anyway, as they aren't now. As I wrote in another email, we could create a simple "copy" LPE for this. Your syntax would allow that too, of course, but it would mean nested switches which adds considerable complexity: the non-Inkscape branch of such a nested switch will still be inside an Inkscape branch of the parent switch! It will be cumbersome to decide what to render and what to edit with such a structure. With our current implementation, it's all in a single path element and therefore much simpler and more straightforward.
- it only works in shape tools, not in Node - hopefully your node tool
rewrite will take care of that
I can say with confidence that the new structure would allow my node tool to work with path parameters without introducing even 1 line of code related to LPEs.
I wouldn't say that we have such a plenty of LPE code in our current node tool, and what we have there your tool will need just as well - such as editing parameter paths with a different outline color. One thing that will become simpler is that we will get rid of the "next LPE parameter" button because we will be able to edit all parameters and the path itself at the same time, but this simplification does not depend on which XML structure we use for LPEs.
- if you assign an effect which has its own knotholder, such as
Hatches, you will lose ellipse handles because we can only have one knotholder at a time.
The proposed XML representation would allow one to actually select the parameter objects without the tools knowing they are path effect parameters
- this problem would go away instantly.
Yes, but again, it is desirable that pattern paths use a different outline color and maybe some other presentation or behavior differences (for example it would be nice to edit the pattern not where it is but moved towards the skeleton that uses it), so the node tool will have to learn all this stuff anyway.
Also, your XML example shows only boolean effects where source paths play more or less the same role. What about pattern along path, where one path is skeleton and the other is pattern? How do you identify in your structure which is which?
I do not agree with the plan to turn svg:rect into an svg:path. Using svg:path to represent a rectangle removes even more semantic information from the document and makes life harder for whoever uses our markup.
We should be smart about it and save it as rect when it is representable as rect, and as path otherwise (e.g. when it has LPEs, markers, text on path, etc).
This is possible now, if only we had these effects available. One of them is there but disabled - it's Union.
Flatten Group is not Union. Union performs a boolean 'or' of all shapes whereas flatten is more or less equivalent to the 'combine' operation.
No problem - we can add a "combine" LPE with current implementation.
On top of that, I'm not sure whether the boolean operations are able to also take the second parameter "by reference" so that the result is updated whenever any of the arguments changes.
Booleans are applied to groups, and are updated live when any path inside that group changes, so no problems here.
I agree they are already useful despite their limitations, though maybe not 'super-powerful'. I just think that the simplest way to achieve this 'cleanup and fixing' without introducing LPE-related functions to every tool is to change the XML representation.
Why "every tool"? All we need to do is: teach the Node tool to edit parameter paths (done), teach the knotholder to edit knots of an LPE (done), and make sure all these knotholders and multiple paths are editable simultaneously (is being worked on). No other changes are needed in any tools - all shape tools just use the knotholder and that's it.
Whereas with your implementation, I foresee changes at least in Selector - it will need to be taught how to select stuff inside switches and what to ignore there. All export and rendering code will all have to be updated as well, otherwise it will just export both source paths and the result path on top of one another. That's quite some rewriting to do, for no clear gain that I can see.
participants (5)
-
unknown@example.com
-
Arcadie M. Cracan
-
bulia byak
-
jf barraud
-
Krzysztof Kosiński