Verbose Path Syntax

Issue:

In the initial days of FXG 1.0 development, Path elements could specify their data in two ways:

  1. As a short-hand syntax, similar to SVG, ie:
<Path data="M 3.0 0.0 L 3.0 1.0 L 4.0 1.0 L 4.0 2.0 C" />

      2. As a long-hand, more verbose but readable, syntax , ie:  

<Path>
 <segments>
  <MoveSegment x="3" y="0" />
  <LineSegment x="3" y="1" />
  <LineSegment x="4" y="1" />
  <LineSegment x="4" y="2" />
  <CloseSegment />
 </segments>
</Path>

The two above were equivalent.

We then rev'ed FXG 1.0 to only allow short-hand syntax since that was what the CS tools were generating, and it did not seem necessary to the FXG stakeholders to have two ways of specifying the same data commands.

We'd like to go ahead and make this change in MXML graphics as well. We would like to make internal the path segment classes. This would mean the segment classes would not be usable in markup, nor would they be externally accessible to developers as typed segment objects. The short-hand syntax was always parsed into typed objects that were stored in the Path.segments property. Users could poke into the segments array to access individual path commands as MoveSegment object, LineSegment objects, etc. We'd be removing this property as well as other segment-associated API's. The full list is documented in the Resolutions section below.

The classes in question being removed from markup/external access are:

spark.primitives.PathSegment;
spark.primitives.CloseSegment;
spark.primitives.CubicBezierSegment;
spark.primitives.LineSegment;
spark.primitives.MoveSegment;
spark.primitives.QuadraticBezierSegment;

The impetus behind this move is two-fold:

1. No concrete use-cases/compelling reason that benefited having these classes laying around for consumption in markup that couldn't be solved in other ways, like re-setting Path.data. This begged the question - why have these classes at all? If we want to add them in later, that's fine - but we didn't want to be boxed into supporting them if we do deem them unnecessary at a later date. Its important to note that there was one use-case we wrestled with that is not possible if we yank the segment classes. Currently, developers can use data-binding with the verbose path syntax to animate individual path segments. For example, something like this:

<HSlider minimum="0" maximum="10" id="hsl" liveDragging="true"/>

<Path id="p" width="100" height="100">
	<segments>
	<MoveSegment x="3" y="{hsl.value}" />
  	<LineSegment x="3" y="1" />
  	<LineSegment x="4" y="1" />
  	<LineSegment x="4" y="2" />
  	<CloseSegment />
	</segments>
 <stroke>
 	<SolidColorStroke color="#FFCC00" />
 </stroke>
</Path>

If we yank the path segment classes, this will not be possible. Instead, users would have to reset the data property over time to represent the move segment changing position. To counter the unwieldiness of this, as a possible B-feature we should look into adding API's that let you poke into a Path data's parsed representation to access and modify individual data commands. (Note for Flex 4, we'll be adding a data structure to cache the parsing of a Path's data, but its still a B-feature to add API's to modify individual data commands. This is tracked via bug SDK-19713)

By adding this in as a helpful utility, we can make the work as performant as we desire.

2. Savings with respect to SWF size, memory and runtime performance. These are quantified in the Performance section below. 

Performance Analysis:

SWF Size: We are essentially creating the same number of classes behind the scenes, they are now just private internal classes. Should there be a savings? There seems to be, but its nominal. The following test was ran to try to gauge the potential savings - with the path segment classes collapsed as internal classes, and various API's related to segment management yanked, we compiled a simple Flex application with a single Path defined in it. The same example was compiled against trunk. The SWF size difference was measured:

With path segment classes publicly accessible SWF Size: 1134780 bytes

Without path segment classes publicly accessible SWF Size: 1132379 bytes

Total Savings: 2401 bytes.

Memory:  Since essentially we are creating the same number of classes behind the scenes, the incremental gains we are making is by reducing event listeners. This will be a drop in the bucket. I tried to quantify the gain roughly by exaggerating my testcase and querying System.totalMemory. I render 1000 Paths and poll System.totalMemory before the rendering and after (I force a call to the GC via the localConnection hack to force the GC to run). The test happens in both trunk and the build that contains the collapsing of the Path segment classes and removal of segment-management API's/event listening. The results are below.

With path segment classes publicly accessible, memory used to render 1000 data-heavy Paths: 4,403,200 bytes

Without path segment classes publicly accessible, memory used to render 1000 data-heavy Paths: 4,217,088 bytes

Difference: 186,112 bytes. So, there is approximately a 186 byte savings per Path with regards to memory consumption when using a build where Path segments are not accessible as typed objects.

Runtime Performance: I tried quantifying how long it takes to realize a Path using the short-hand syntax vs. the verbose syntax. In the case of the verbose tags, the compiler will generate factory method, one for each tag and call them all in succession and then shove everything into an array. In the shorthand syntax, we parse the string and walk the results and create the classes programmatically. I used the following test to try to gauge runtime performance. Using a build with the verbose path syntax classes made internal, and all segment-related management API's yanked, I measured how long it took to draw 1000 data-heavy Paths. I used getTimer() calls to flank the rendering process and get a handle on time to execute. I then ran the same test against trunk.

With path segment classes publicly accessible, time to create 1000 heavy Paths: 134 ms

With path segment classes publicly accessible, time to create 1000 heavy Paths: 106 ms

Total savings: 28 ms

Proposal

Collapse the PathSegment class and its derivatives as private classes in Path and hide them from external use. This includes:

  • Fold the following segment classes as internal classes in Path.
    spark.primitives.PathSegment;
    spark.primitives.CloseSegment;
    spark.primitives.CubicBezierSegment;
    spark.primitives.LineSegment;
    spark.primitives.MoveSegment;
    spark.primitives.QuadraticBezierSegment;
  • Remove all the above-stated path segment's classes extensions of EventDispatcher, dispatching of events when properties change, Bindable metadata, Inspectable metadata including notification methods like Path.segmentChanged, etc.
  • Remove Path.segment
You must be logged in to comment.

  1. Mar 17, 2009

    Christophe JOLIF says:

    If SVG does only have the short-hand syntax it does have segments API to deal at...

    If SVG does only have the short-hand syntax it does have segments API to deal at runtime with the path shape. I can imagine use-cases where this can be handy even though this is not a must-have for a first release. If that is only available through an API I guess you can make sure it does not hurt performances as soon as it is not used?

    1. Mar 20, 2009

      Deepa Subramaniam says:

      Hey Christophe, yes, that is the thought. That we would add in an API (either in...

      Hey Christophe, yes, that is the thought. That we would add in an API (either in this release or a subsequent one) that would sit on top of the short-hand syntax specified on a Path and allow users to mutate individual segments. For now, once this ARB change is made, access to individual drawing commands need to be parsed out of the data property by end users themselves, or else they can just re-set the data property with the new drawing commands.

  2. Mar 17, 2009

    Jason Stafford says:

    I am not voting against this proposal per se, but I would like to suggest a use ...

    I am not voting against this proposal per se, but I would like to suggest a use case for having programmatic access to path data. I am working on a diagramming tool available at http://myWebspiration.com. If you want to look at it, sign up today, because we are temporarily closing the beta on 3/18/09.

    In myWebspiration.com we have symbols (aka bubbles, aka nodes) which are connected by links (aka lines). As a user moves a symbol, the links adjust as needed to stay connected to their associated symbols in real time.

    We currently implement this by redrawing the entire link, but conceptually we are really just adjusting existing segments. In the future I would love to be able to implement this as manipulating a path because I think that there would be some performance improvements.

    1. Mar 20, 2009

      Deepa Subramaniam says:

      Very cool Jason! And yes, the API that is briefly mentioned in the proposal (the...

      Very cool Jason! And yes, the API that is briefly mentioned in the proposal (the one that would live on top of the short-hand syntax and offer access to individual drawing commands for mutation) would aid your project. This is something that would be nice to squeeze into this release, but may have to wait for a subsequent release. Of course, there's nothing stopping end users from writing this logic on top of Path in the meantime.

  3. Mar 18, 2009

    Tom Chiverton says:

    This too me looks like the equiv. in MXML of making children of an object privat...

    This too me looks like the equiv. in MXML of making children of an object private, and adding methods like getChildAt(int) and getNumChildren():int to the API.
    This seems a sensible compromise, though the more compact syntax is less clear (to me as a coder).

    1. Mar 20, 2009

      Deepa Subramaniam says:

      Just a note here that I don't really imagine too many people hand-coding Path da...

      Just a note here that I don't really imagine too many people hand-coding Path data. If they are, the complexity of it will be minimal (and at that point, the hard part when hand-coding Path does not come from using the short-hand vs long-hand syntax but mentally constructing the drawing commands to realize your Path).

  4. Aug 19, 2009

    Christophe JOLIF says:

    Now that Flex 4 has been postponed and that you get more time, is that possible ...

    Now that Flex 4 has been postponed and that you get more time, is that possible your introduce a segment API for path? Indeed since my last comment I have learned another team here is extensively relying on segments API and will miss is a lot when updating to new build where the API has been removed.

  5. Aug 24, 2009

    Christophe JOLIF says:

    FYI I have checked why we use the API and not the data property. Actually that w...

    FYI I have checked why we use the API and not the data property. Actually that was for performances reasons. With complex path data (long strings), the regexp that are executed when setting the data are decreasing performances. So we really need access to the API, or at least optimized version of data() that would not do all the current checks.

  6. Sep 23, 2009

    Daniel Freiman says:

    Sorry for coming late to the party. I've been using the milestone build for awh...

    Sorry for coming late to the party. I've been using the milestone build for awhile and this didn't make it onto my radar. Personally, removing these classes and direct access to segments is a significant problem for me. One of the reasons I was so happy about Spark was that it had PathSegment classes. I've got a lot of UI that interacts with specific path segments and I was also very close to having some interesting interactions between Paths and TLF. That's all shot now, until I find some workarounds. I don't expect Adobe to change structure just for me, but is there a way I can at least know that my use cases are getting to people who could affect change if they were to agree with me? (At minimum, making segments protected or mx_internal would be a huge help.)

  7. Oct 21, 2009

    Rick Bullotta says:

    Very disappointing decision. Regarding the statement "No concrete use-cases/com...

    Very disappointing decision. Regarding the statement "No concrete use-cases/compelling reason that benefited having these classes laying around for consumption in markup that couldn't be solved in other ways, like re-setting Path.data." - this is like saying "there's no reason to have MXML since it could be solved in other ways, like writing ActionScript". There are countless example of dynamic, animated graphics that would benefit from a more elegant, declarative, and independently manipulatable model for graphics and paths. Think about the horrific code that you're requiring developers to write in AS to dynamically reconstruct the very obtuse syntax of the Path's "data" property. I empathise with your concerns regarding performance, but rather than ignore them, why not take them on head on and try to address them? Or leave the alternative mechanism in place with the documentation/caveat that there is a performance penalty? In the end, I do not think it is valuable to sacrifice developer productivity and application "cleanliness" for perceived inabilities to address some indeterminate performance requirement. I urge you to reconsider and to reconsider some of the other changes/limitations that have been introduced into FXG between the time it was first conceived/specified and the present.

    Thanks for your re-consideration,

    Rick Bullotta
    CTO/Co-Founder
    Burning Sky Software