w3c / svgwg

SVG Working Group specifications
Other
711 stars 133 forks source link

Verbose SVG for improved human readability #340

Open Crissov opened 7 years ago

Crissov commented 7 years ago

I wish there was a supplementary superset of SVG that made the rect, circle/ellipse, line, polyline and polygon optionally have a number of p children. The number of mandatory and optional p children varies by element type.

The p element is a point or coordinate, specified either absolutely by x and y attributes or relatively by dx and dy attributes. They all default to 0 initially, but x and y inherit the computed values of the previous sibling – or cousin in path – point. If both, x and dx or y and dy, are specified the relative values are added to the absolute ones to give the computed coordinate.

<rect><!-- 1 absolute and 1 relative point -->
    <p x="rect@x" y="rect@y"/>
    <p dx="rect@width" dy="rect@height"/>
    <!-- not sure how corner radii specified with rect@rx and rect@ry should be dealt with -->
</rect>
<rect><!-- 2 absolute points specify two corners -->
    <p x="rect@x" y="rect@y"/>
    <p x="rect@x + rect@width" y="rect@y + rect@height"/>
</rect>

<circle><!-- 2 points: center and on arc, `circle` aliases `ellipse` -->
    <p x="circle@cx" y="circle@cy"/>
    <p dx="circle@r" dy="circle@r"/>
</circle>
<circle>
    <p x="circle@cx" y="circle@cy"/>
    <p dx="circle@r"/>
</circle>
<circle>
    <p x="circle@cx" y="circle@cy"/>
    <p dy="circle@r"/>
</circle>
<ellipse>
    <p x="ellipse@cx" y="ellipse@cy"/>
    <p dx="ellipse@rx" dy="ellipse@ry"/>
</ellipse>

<line>
    <p x="line@x1" y="line@y1"/>
    <p x="line@x2" y="line@y2"/>
</line>
<line>
    <p x="line@x1" y="line@y1"/>
    <p dx="line@x2 - line@x1" dy="line@y2 - line@y1"/>
</line>

I believe a superset syntax like this would make editing by hand, especially reading, easier. The transformations to the legacy syntax are straightforward, but for these elements the argument is probably not convincing enough. It gets handier with elements that support more, i.e. an arbitrary number of points:

<polyline><!-- `polyline` aliases `line` -->
    <p x="polyline@points1" y="polyline@points2"/><!--+-->
</polyline>
<polyline>
    <p x="polyline@points1" y="polyline@points2"/>
    <p dx="polyline@points3 - polyline@points1" dy="polyline@points4 - polyline@points2"/><!--*-->
</polyline>
<polygon>
    <p x="polygon@points1" y="polygon@points2"/><!--+-->
</polygon>
<polygon>
    <p x="polygon@points1" y="polygon@points2"/>
    <p dx="polygon@points3 - polygon@points1" dy="polygon@points4 - polygon@points2"/><!--*-->
</polygon>

The path element on the other hand would have an additional layer of intermediate children that specify the drawing method otherwise encoded as a single case-sensitive letter inside its d attribute. Making its compact d notation more readable and editable is the main motivation for this issue.

<path>
    <!-- `moveto` -->
    <move><p x="M1" y="M2"/></move>
    <move><p dx="m1" dy="m2"/></move>
    <!-- `closepath` -->
    <close/><!--Z/z-->
    <!-- `lineto` -->
    <line><p x="L1" y="L2"/><!--+--></line>
    <line><p dx="l1" dy="l2"/><!--+--></line>
    <line><p x="H"/></line>
    <line><p dx="h"/></line>
    <line><p y="V"/></line>
    <line><p dy="v"/></line>
    <!-- cubic Bézier -->
    <curve><p x="C1" y="C2"/><p x="C3" y="C4"/><p x="C5" y="C6"/><!--+--></curve>
    <curve><p dx="c1" dy="c2"/><p dx="c3" dy="c4"/><p dx="c5" dy="c6"/><!--+--></curve>
    <spline><p x="S1" y="S2"/><p x="S3" y="S4"/><!--+--></spline>
    <spline><p dx="s1" dy="s2"/><p dx="s3" dy="s4"/><!--+--></spline>
    <!-- quadratic Bézier -->
    <qurve><p x="Q1" y="Q2"/><!--+--></qurve>
    <qurve><p dx="q1" dy="q2"/><!--+--></qurve>
    <t><p x="T1" y="T2"/><p x="T3" y="T4"/><p x="T5" y="T6"/><!--+--></t>
    <t><p dx="t1" dy="t2"/><p dx="t3" dy="t4"/><p dx="t5" dy="t6"/><!--+--></t>
    <!-- elliptical arc -->
    <arc rotation="A3" large-arc="A4" sweep="A5"><p dx="A1" dy="A2"/><p x="A6" y="A7"/></arc>
    <arc rotation="a3" large-arc="a4" sweep="a5"><p dx="a1" dy="a2"/><p dx="a6" dy="a7"/></arc>
    <!-- Catmull-Rom -->
    <rom><p x="R1" y="R2"/><p x="R3" y="R4"/><!-- --><p dx="R5" dy="R6"/><!--*--></rom>
    <rom><p dx="r1" dy="r2"/><p dx="r3" dy="r4"/><!-- --><p dx="r5" dy="r6"/><!--*--></rom>
    <!-- bearing ? -->
    <bear a="B"/>
    <bear da="b"/>
    <bear><p x="cos(B)" y="sin(B)"/></bear>
    <!-- * -->
</path>
longsonr commented 7 years ago

What if there are multiple identical children? Sounds like a nightmare for performance, all the parents would need to listen for child content changes. I think I'm going to decline to implement this or see it implemented natively in Firefox. You could always write some kind of XSLT transform that implements your syntax and converts it to SVG native. Good luck with that as I think it's the way forward for you here.

Herst commented 7 years ago

This would be a bit like what content MathML is to presentation MathML? But I see even less for this here since e.g. the semantic aspect for screen readers isn't there.

struddysit commented 7 years ago

I'm not sure why you would want to do it this way.

Crissov commented 7 years ago

My main issue is with the syntax within the d attribute of the path element, the rest are mostly logical conclusions from that. The existing syntax makes code concise but hardly readable, especially if minified. Using child elements, you could always immediately identify points from other values, for instance.

File size is not an issue for this particular use case. I also doubt that the difference is huge after compression has been applied, especially when compared to non-minified hand-written code which includes optional but helpful commas and whitespace.

I’m not strongly attached to the proposed p moniker, which stems from point obviously. An abbreviation of coordinate would work just as well.

If one of SVG’s strongest advantages wasn’t its readability by humans, it could have been a more efficient non-XML, binary format in the first place.

The syntax extension I am proposing would be an optional superset that can be converted easily to the traditional style. It is just more verbose.

@longsonr is right, I could preprocess a custom syntax into standard SVG, but that would benefit only myself, hence my suggestion to standardize it. My point is that parts of the current syntax are hostile to potential new authors. Path syntax also does not afford helpful GUI solutions.

I don’t get the JavaScript argument. Yes, additional nodes make the tree more complex, but they also make working with them more straightforward. Imagine a graphical image editor: if you are dragging an anchor point, it makes more sense and feels much cleaner to alter a single child element than to update a random part of a textual attribute node. In other words, it is a more appropriate mapping of the mental and visitation models into code. This is also true in particular for the difference between absolute and relative coordinates that are currently realized by the case of single letters.

struddysit commented 7 years ago

Given that most people would edit or create SVGs in a program like Inkscape, Adobe Illustrator, CorelDraw etc, readability is a non-issue. If it is for you, I would suggest just writing a script to insert a new line after each segment of a path. E.g.

<path 
   fill="orange"
   stroke="black"
   stroke-width="3" 
   d="M 100 100 
        L 300 100
        L 200 300
        z
        "
/>

The current d attribute in path is logical. You can follow along each step of the way and know what it is doing, especially if everything is along relative paths. Mixing absolute points in requires more of an understanding about where the path is located overall, which can make things difficult. If you are struggling, why not throw in a quick h5h-10h5v5v-10v5 (adjust size to appropriate value), when you're editing, so you get a big + when displaying the SVG.

As to the javascript, if it expects an SVG element with AttributeNS, but your non standard SVG is served, it would need to instead loop through child nodes looking for values. So existing code would break.

boggydigital commented 6 years ago

Not blocking updated 2.0 CR publication - assigning 2.1 WD milestone

Crissov commented 4 years ago

I remembered this proposal today, because it would provide something for free that SVG does not have yet (as far as I know): named coordinates and reusable dimensions.

<defs>
  <p id="wxh" dx="123" dy="45"/>  
  <p id="xy1" x="67" y="89"><desc>upper left corner of rectangle</desc></p>  
  <p id="xy2" x="190" y="134"/>  
</defs>

<rect><!-- 1 absolute and 1 relative point -->
  <use xlink:href="#xy1"/>
  <use xlink:href="#wxh"/>
</rect>
<rect><!-- 2 absolute points specify two corners -->
  <use xlink:href="#xy1"/>
  <use xlink:href="#xy2"/>
</rect>
Crissov commented 4 years ago

https://github.com/w3c/csswg-drafts/issues/5674 reminded me of this issue today.

Instead of a universal <p> element, it could also make sense to have an absolute <to> or <abs> element and a relative <by> or <rel> element with unified x and y attributes:

<defs>
  <by id="wxh" x="123" y="45"/>  
  <to id="xy1" x="67" y="89"/>  
  <to id="xy2" x="190" y="134"/>  
</defs>
<defs>
  <rel id="wxh" x="123" y="45"/>  
  <abs id="xy1" x="67" y="89"/>  
  <abs id="xy2" x="190" y="134"/>  
</defs>
longsonr commented 4 years ago

You can achieve some of what you want with CSS variables on the elements that have their positioning as CSS properties i.e. circle, ellipse, rect

Crissov commented 2 years ago

Just for the record, Fitbit supports a proprietary arc element (and some other extensions) in their clockface SVGs:

Draws an arc, circular or elliptical.

Attributes

  • arc-width: arc thickness in pixels, or %-expression. Maximum 31.
  • start-angle: start angle in degrees, with 0 at 12 o'clock.
  • sweep-angle: length of the arc in degrees.