Re: [whatwg] Canvas line styles comments

2008-02-09 Thread Ian Hickson
On Sat, 2 Feb 2008, Philip Taylor wrote:
 
 The lineCap attribute defines the type of endings that UAs shall place 
 on the end of lines. - it seems weird to use shall, since this is the 
 only place in the spec (except the list of RFC2119 keywords) that uses 
 it. The other line* properties don't try define to conformance 
 requirements like that (e.g. they say The lineWidth attribute gives the 
 width of lines which is only informative), so I can't tell whether the 
 lineCap one is trying to be a requirement.

Woops. Fixed.


 The lineJoin attribute defines the type of corners that that UAs will 
 place where two lines meet. - s/that that/that/

Fixed.


 A join exists at any point in a subpath shared by two consecutive pairs 
 of lines. - should be two consecutive lines or a consecutive pair of 
 lines.

Fixed.


 In addition to the point where the join occurs, two additional points 
 are relevant to each join: the corners found half the line width away 
 from the join point, perpendicular to the two lines joining at the join 
 point. - I'm not sure what that means. Nothing can be perpendicular to 
 both of the two lines (unless they're parallel). For each line, there 
 are the two corners half the line width away from the join point 
 perpendicular to that line, but that gives four corners in total.

I've tried to explain it better. Let me know if it's not ok.


 A filled triangle connecting ... with the third point of the triangle 
 being the point of the join itself (where the lines touch on the inside 
 of the join), must be rendered at all joins. - the inside of the join 
 bit seems unhelpful and unclear (since it's not the opposite of the 
 outside of the join) - it'd be better just to say ... being the join 
 point, must be ..., since that's the term used earlier for that point.

That simplification is a great idea. Done.


 The round value means that a filled arc connecting the two corners on 
 the outside of the join, with the diameter equal to the line width and 
 the origin at the point of the join, must be rendered at joins. - if I 
 was being pedantic (which I am) I'd say there's two possible arcs 
 connecting those two corners (one clockwise, one anticlockwise), so it 
 should specify which one is meant. But I don't know how to easily say 
 that, and an implementor would have to be silly to do it the wrong way, 
 so maybe a precise definition isn't needed.

I have tried to out-pedanticise you.


 Should lineJoin='round';moveTo(0,0);lineTo(100,0);lineTo(0,0);stroke() 
 draw a semicircle at (100,0) pointing rightwards? There is no outside 
 of the join there, so the spec doesn't say what should happen.

Fixed.


 The miter value means that a filled four-sided polygon must be rendered 
 at the join, with two of the lines being the perpendicular edges of the 
 joining lines, ... - the miter-polygon lines aren't the perpendicular 
 edges - they're only half of each edge (between the join point and the 
 outside corners). It's probably easier to define the polygon's points 
 (being the join point, the two outside corners, and the point where the 
 two continuated outside edges intersect).

I've tried redefining this using a triangle.

-- 
Ian Hickson   U+1047E)\._.,--,'``.fL
http://ln.hixie.ch/   U+263A/,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'


Re: [whatwg] Canvas line styles comments

2008-02-03 Thread Kristof Zelechovski
It should perhaps be explained that the joining arc 
must be outside the convex hull 
locally around the terminating points, 
which condition holds for the ccw arc only.

Chris

-Original Message-
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of Philip Taylor
Sent: Saturday, February 02, 2008 11:21 PM
To: Kristof Zelechovski
Cc: WHATWG
Subject: Re: [whatwg] Canvas line styles comments

On 02/02/2008, Kristof Zelechovski [EMAIL PROTECTED] wrote:
 You considered the convex hull of the original lines to get that paradox;
 I had the stroke path segments in mind.
 (Stroke path segments are the path equivalent of the stroked curve
 when the stroke operator is not allowed and must be replaced by the fill
 operator).
 Each line corresponds to two parallel stroke path segments;
 two of them intersect and the other two get joint with an arc.
 One of the possible arcs is in the convex hull of those stroke path
 segments.

If the two lines are very short, their stroke paths will (if I
understand correctly) look like

   .-.
   | |
   | |
   | |
 .-|-*---.
 '-|-|---'
   | |
   | |
   '-'

where the * is the join point and the short lines are the two parallel
stroke path segments of each line. Then the convex hull is nearly a
square rotated by 45 degrees, like

   .-.
  /| |'-
/  | |  '-
  /| |'-.
 .-|-*---.
 '-|-|---'
  '.   | |.-'
'-.| |_.-'
   '-'

and so an arc with radius lineWidth/2 from the rightmost point going
clockwise to the upmost point will not be contained entirely within
that nearly-square. So neither arc is within the convex hull.

-- 
Philip Taylor
[EMAIL PROTECTED]




Re: [whatwg] Canvas line styles comments

2008-02-02 Thread Philip Taylor
Some comments on the newly modified version:


The lineCap attribute defines the type of endings that UAs shall
place on the end of lines. - it seems weird to use shall, since
this is the only place in the spec (except the list of RFC2119
keywords) that uses it. The other line* properties don't try define to
conformance requirements like that (e.g. they say The lineWidth
attribute gives the width of lines which is only informative), so I
can't tell whether the lineCap one is trying to be a requirement.


The lineJoin attribute defines the type of corners that that UAs will
place where two lines meet. - s/that that/that/


A join exists at any point in a subpath shared by two consecutive
pairs of lines. - should be two consecutive lines or a consecutive
pair of lines.


In addition to the point where the join occurs, two additional points
are relevant to each join: the corners found half the line width away
from the join point, perpendicular to the two lines joining at the
join point. - I'm not sure what that means. Nothing can be
perpendicular to both of the two lines (unless they're parallel). For
each line, there are the two corners half the line width away from the
join point perpendicular to that line, but that gives four corners in
total.

I suppose it'd be alright to say there's four corners, and then talk
about the two corners on the outside of the join since the meaning
of outside is obvious enough even if it's not defined (at least when
the lines aren't parallel).


A filled triangle connecting ... with the third point of the triangle
being the point of the join itself (where the lines touch on the
inside of the join), must be rendered at all joins. - the inside of
the join bit seems unhelpful and unclear (since it's not the opposite
of the outside of the join) - it'd be better just to say ... being
the join point, must be ..., since that's the term used earlier for
that point.


The round value means that a filled arc connecting the two corners on
the outside of the join, with the diameter equal to the line width and
the origin at the point of the join, must be rendered at joins. - if
I was being pedantic (which I am) I'd say there's two possible arcs
connecting those two corners (one clockwise, one anticlockwise), so it
should specify which one is meant. But I don't know how to easily say
that, and an implementor would have to be silly to do it the wrong
way, so maybe a precise definition isn't needed.

Should lineJoin='round';moveTo(0,0);lineTo(100,0);lineTo(0,0);stroke()
draw a semicircle at (100,0) pointing rightwards? There is no outside
of the join there, so the spec doesn't say what should happen.


The miter value means that a filled four-sided polygon must be
rendered at the join, with two of the lines being the perpendicular
edges of the joining lines, ... - the miter-polygon lines aren't the
perpendicular edges - they're only half of each edge (between the join
point and the outside corners). It's probably easier to define the
polygon's points (being the join point, the two outside corners, and
the point where the two continuated outside edges intersect).

-- 
Philip Taylor
[EMAIL PROTECTED]


Re: [whatwg] Canvas line styles comments

2008-02-02 Thread Kristof Zelechovski
A pair cannot be consecutive unless it follows after another pair, 
which would be irrelevant anyway.

The rounding arc should be chosen 
so that it is not contained in the convex hull of the stroke path segments 
terminated at the points where the arc begins.

Chris

-Original Message-
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of Philip Taylor
Sent: Saturday, February 02, 2008 8:48 PM
To: Ian Hickson
Cc: WHATWG
Subject: Re: [whatwg] Canvas line styles comments

Some comments on the newly modified version:


[snip]


A join exists at any point in a subpath shared by two consecutive
pairs of lines. - should be two consecutive lines or a consecutive
pair of lines.


[snip]


The round value means that a filled arc connecting the two corners on
the outside of the join, with the diameter equal to the line width and
the origin at the point of the join, must be rendered at joins. - if
I was being pedantic (which I am) I'd say there's two possible arcs
connecting those two corners (one clockwise, one anticlockwise), so it
should specify which one is meant. But I don't know how to easily say
that, and an implementor would have to be silly to do it the wrong
way, so maybe a precise definition isn't needed.

Should lineJoin='round';moveTo(0,0);lineTo(100,0);lineTo(0,0);stroke()
draw a semicircle at (100,0) pointing rightwards? There is no outside
of the join there, so the spec doesn't say what should happen.


[snip]

-- 
Philip Taylor
[EMAIL PROTECTED]




Re: [whatwg] Canvas line styles comments

2008-02-02 Thread Philip Taylor
On 02/02/2008, Kristof Zelechovski [EMAIL PROTECTED] wrote:
 The rounding arc should be chosen
 so that it is not contained in the convex hull of the stroke path segments
 terminated at the points where the arc begins.

I believe I can see the idea there, but I can't quite tell what that
phrase means about terminating. The contained within also seems
inaccurate, because e.g.
lineWidth=100;moveTo(0,0);lineTo(1,0);lineTo(1,1) would result in a
convex hull that doesn't contain either arc, though I think it'd be
alright if said does not intersect instead.

A possible alternative that seems simpler and (I think) correct
(except in the special parallel case): The rounding arc should be
chosen so that if it was closed, it would not contain the join point.

-- 
Philip Taylor
[EMAIL PROTECTED]


Re: [whatwg] Canvas line styles comments

2008-02-02 Thread Kristof Zelechovski
You considered the convex hull of the original lines to get that paradox; 
I had the stroke path segments in mind.  
(Stroke path segments are the path equivalent of the stroked curve 
when the stroke operator is not allowed and must be replaced by the fill
operator).
Each line corresponds to two parallel stroke path segments; 
two of them intersect and the other two get joint with an arc.
One of the possible arcs is in the convex hull of those stroke path
segments.

While talking intersection instead of convexity is mathematically simpler, 
convexity is what is intended, intersection may be a technicality.  
I think the specification should specify the intention and not the technical
means wherever possible.

Cheers,
Chris

-Original Message-
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of Philip Taylor
Sent: Saturday, February 02, 2008 10:25 PM
To: Kristof Zelechovski
Cc: WHATWG; Ian Hickson
Subject: Re: [whatwg] Canvas line styles comments

On 02/02/2008, Kristof Zelechovski [EMAIL PROTECTED] wrote:
 The rounding arc should be chosen
 so that it is not contained in the convex hull of the stroke path 
 segments terminated at the points where the arc begins.

I believe I can see the idea there, but I can't quite tell what that phrase
means about terminating. The contained within also seems inaccurate,
because e.g.
lineWidth=100;moveTo(0,0);lineTo(1,0);lineTo(1,1) would result in a convex
hull that doesn't contain either arc, though I think it'd be alright if said
does not intersect instead.

A possible alternative that seems simpler and (I think) correct (except in
the special parallel case): The rounding arc should be chosen so that if it
was closed, it would not contain the join point.

--
Philip Taylor
[EMAIL PROTECTED]




Re: [whatwg] Canvas line styles comments

2008-02-02 Thread Philip Taylor
On 02/02/2008, Kristof Zelechovski [EMAIL PROTECTED] wrote:
 You considered the convex hull of the original lines to get that paradox;
 I had the stroke path segments in mind.
 (Stroke path segments are the path equivalent of the stroked curve
 when the stroke operator is not allowed and must be replaced by the fill
 operator).
 Each line corresponds to two parallel stroke path segments;
 two of them intersect and the other two get joint with an arc.
 One of the possible arcs is in the convex hull of those stroke path
 segments.

If the two lines are very short, their stroke paths will (if I
understand correctly) look like

   .-.
   | |
   | |
   | |
 .-|-*---.
 '-|-|---'
   | |
   | |
   '-'

where the * is the join point and the short lines are the two parallel
stroke path segments of each line. Then the convex hull is nearly a
square rotated by 45 degrees, like

   .-.
  /| |'-
/  | |  '-
  /| |'-.
 .-|-*---.
 '-|-|---'
  '.   | |.-'
'-.| |_.-'
   '-'

and so an arc with radius lineWidth/2 from the rightmost point going
clockwise to the upmost point will not be contained entirely within
that nearly-square. So neither arc is within the convex hull.

-- 
Philip Taylor
[EMAIL PROTECTED]


Re: [whatwg] Canvas line styles comments

2008-01-24 Thread Ian Hickson
On Tue, 19 Jun 2007, Philip Taylor wrote:
 
 For lineJoin, the term joins is used but not properly defined
 (except indirectly as where two lines meet). Given the
 implementations, this should be something like:
 
 For each subpath, a join exists at the point shared by each
 consecutive pair of lines. If the subpath is closed, then a join also
 exists at its first point (equivalent to its last point) connecting
 the first and last lines in the subpath.
 

Added something close to that.


 There are no conformance criteria for rendering lineCap.

Fixed, sort of.


 The definition of 'miter' is incorrect: it seems to say the miter gets 
 truncated into a more-sided polygon if it would exceed miterLimit, but 
 the behaviour of implementations is to revert to 'bevel' rendering 
 instead.
 The definition of 'round' for lineJoin is slightly incorrect, since it 
 talks about adding a filled arc when it needs to be a filled circle 
 sector (or an arc plus a triangle).

Fixed.


 The definition for 'stroke' says The stroke() method must stroke each 
 subpath of the current path in turn, using the strokeStyle, lineWidth, 
 lineJoin, and (if appropriate) miterLimit attributes. That list should 
 include lineCap.

Fixed.


 The lineWidth attribute gives the default width of lines, in coordinate 
 space units. - why default?

Removed.


 The expression the point where the inside edges of the lines touch 
 doesn't make sense to me. (Actually, it did make sense for a while, but 
 then I realised it was an incorrect sense).

Fixed.


 I think the problem is in being ambiguous about the distinction between 
 geometric lines (which are infinitely thin and just a description of a 
 path through space) and graphical lines (which are a thick filled shape, 
 defined by their edges (which are geometric lines)) - the rendering 
 details are describing how to convert the first sort of line into the 
 second sort of line, but that seems to be made unclear.

 I believe it would be clearer to use the term line only in the first 
 sense (so ctx.lineTo adds a line to the subpath, and ctx.fill fills the 
 area enclosed by the path's lines, etc), and the term stroke [or a 
 better name, since I don't really like this one, but I can't think of 
 anything else] for the second sense (so ctx.stroke calculates and 
 renders strokes, which are shapes that are based on the path's lines and 
 widths and caps and joins). There also seems to be a danger of confusion 
 between lines (like a single straight/arc/Bézier line segment) and 
 subpaths, like in the definition of what lineCap applies to.

Are there any bits that are really still confusing? I'd rather not make 
sweeping changes to the terminology like this, I'd almost certainly get it 
wrong and make matters worse.

(I agree that this section has suboptimal conformance requirements. It's 
one of the first sections I wrote for this spec, and it shows. However, 
I'd like to limit the fixes to blatent mistakes and areas where interop is 
failing due to the spec.)


 (Is it worth having diagrams (kind of like 
 http://canvex.lazyilluminati.com/misc/linejoin.png), so normal people 
 can tell what the interesting bits here actually mean? Or is that best 
 left for tutorials and user reference guides?)

Diagrams would be great. I plan on doing a pass with adding diagrams and 
examples much later, once the spec is stable, but feel free to provide 
unstylised diagrams in the meantime. :-)


 There are some other issues I'm currently aware of, possibly requiring 
 more complexity:
 
 What happens when a stroked path has zero length, in terms of drawing 
 the line caps/joins? In particular, square caps are impossible because 
 the line does not have a defined direction (assuming we're not having 
 dashed paths for now). In Firefox 2 and Opera, nothing is drawn for 
 zero-length paths. In Firefox 3 and Safari, round caps/joins are drawn 
 (because the direction of the line doesn't matter in that case, so the 
 output is well-defined), and nothing else is drawn.

I've added a line that says that zero-length line segments and pruned 
before stroking, which as far as I can tell makes Firefox 2 and Opera's 
behaviour correct.


 What happens when a stroked path contains a line with zero length, 
 between non-zero-length lines? As far as I can tell, zero-length lines 
 never have any effect (e.g. line-joins get drawn between two 
 non-consecutive non-zero-length lines if they have only zero-length 
 lines between them, so the earlier suggestion for defining 'join' is 
 wrong) - except when the path has no non-zero-length lines in it, in 
 which case the presence of a zero-width line causes round caps to be 
 drawn in FF3/Safari. (...except in FF3 when it's a zero-length 
 quadratic/Bézier curve). Maybe it'd be best just to require that lines 
 with zero length are never added to the subpath - so if you don't add 
 any non-zero-length ones, the subpath will be empty and won't get drawn, 
 which is slightly 

[whatwg] Canvas line styles comments

2007-06-19 Thread Philip Taylor

Lines are great fun.

See http://canvex.lazyilluminati.com/misc/lines.html for a random
collection of demonstrations relating to the stuff below.


For lineJoin, the term joins is used but not properly defined
(except indirectly as where two lines meet). Given the
implementations, this should be something like:

For each subpath, a join exists at the point shared by each
consecutive pair of lines. If the subpath is closed, then a join also
exists at its first point (equivalent to its last point) connecting
the first and last lines in the subpath.


There are no conformance criteria for rendering lineCap.

The definition of 'miter' is incorrect: it seems to say the miter gets
truncated into a more-sided polygon if it would exceed miterLimit, but
the behaviour of implementations is to revert to 'bevel' rendering
instead. The definition of 'round' for lineJoin is slightly incorrect,
since it talks about adding a filled arc when it needs to be a filled
circle sector (or an arc plus a triangle).

The definition for 'stroke' says The stroke() method must stroke each
subpath of the current path in turn, using the strokeStyle, lineWidth,
lineJoin, and (if appropriate) miterLimit attributes. That list
should include lineCap.

The lineWidth attribute gives the default width of lines, in
coordinate space units. - why default?



The expression the point where the inside edges of the lines touch
doesn't make sense to me. (Actually, it did make sense for a while,
but then I realised it was an incorrect sense). I think the problem is
in being ambiguous about the distinction between geometric lines
(which are infinitely thin and just a description of a path through
space) and graphical lines (which are a thick filled shape, defined by
their edges (which are geometric lines)) - the rendering details are
describing how to convert the first sort of line into the second sort
of line, but that seems to be made unclear.

I believe it would be clearer to use the term line only in the first
sense (so ctx.lineTo adds a line to the subpath, and ctx.fill fills
the area enclosed by the path's lines, etc), and the term stroke [or
a better name, since I don't really like this one, but I can't think
of anything else] for the second sense (so ctx.stroke calculates and
renders strokes, which are shapes that are based on the path's lines
and widths and caps and joins). There also seems to be a danger of
confusion between lines (like a single straight/arc/Bézier line
segment) and subpaths, like in the definition of what lineCap applies
to.

So perhaps it could say something like:


The lineWidth attribute gives the width used for rendering lines, in
coordinate space units. The outline of a rendered stroke must pass
through the points at a distance lineWidth/2 perpendicular to each
point in the line being stroked, and must be closed at each end by a
straight line. [[...because it's good to define what the width
actually means, though I'm not sure if this definition is sufficiently
clear/correct.]]

...

The lineCap attribute defines the type of endings that UAs shall place
on the end of lines. The three valid values are butt, round, and
square. The butt value means that no cap shape will be added to the
lines. [[...since you don't have to do anything extra at this stage -
the earlier paragraph already said how to close the lines at the ends
in a butt-like way.]] The round value means that a semi-circle with
the diameter equal to the line width must be added on to the first and
last points of each unclosed subpath. [[It needs to ignore closed
subpaths - those get joined instead of capped.]] The square value
means that a rectangle with the length of the line width and the width
of half the line width must be placed flat against the edge
perpendicular to the direction of the line, on the first and last
points of each unclosed subpath. ...

...

At each join, if the two lines connected to the join have the same
direction at that point, no line join is rendered. If the two lines
have exactly opposite directions, and lineJoin is round, then a filled
semi-circle must be added with its diameter equal to the line width,
its origin at the join, and its flat edge touching the edges of the
strokes; otherwise, when lineJoin is not round, no line is rendered.
[[It won't make sense to talk about the outside edges at a join if all
the edges are parallel, so these cases need to be handled specially.
It also avoids issues like the miter trying to find an intersection
point between parallel lines.]]

Otherwise, if the two lines do not have equal or opposite directions,
the following rendering steps are performed for the join:

* A filled triangle must be added between the position of the join and
the two corners of the strokes on the outside of the join. [[That
triangular region is shared for all the following variations, so it
seems easier to describe it as separate step.]] [[Things like outside
of the join are not defined but seem clear enough to me.]]

*