Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread m...@mikesolomon.org

On 29 sept. 2012, at 19:54, m...@mikesolomon.org wrote:

 
 On 29 sept. 2012, at 19:53, Keith OHara k-ohara5...@oco.net wrote:
 
 On Sat, 29 Sep 2012 10:30:32 -0700, m...@mikesolomon.org 
 m...@mikesolomon.org wrote:
 
 
 The way you're using tentative is almost exactly how pure properties are 
 used in LilyPond.
 
 Specifically, 'pure-height being the estimated vertical extent before 
 line-breaking, while 'height is its extent after line-breaking.
 
 If there are distinct properties to describe the position at different 
 stages, then each property can be evaluated just once (as HanWen suggested, 
 and as Mike agreed 100%).

More thinking.  I'm not enthusiastic about stages - it is a top down approach 
that locks us into certain points of evaluation.  What if we decided to add or 
get rid of a stage?  Would we need to create things like unpure-pure-containers 
for various stages?  What qualifies as a stage?  Could users make custom stages?

Furthermore, the idea of properties being pure in LilyPond is impossible to 
police.  The code almost guarantees that won't be pure, as once the dim_cache_ 
of a grob is filled, it gives that value instead of the result of a function 
call (see Grob::pure_relative_y_coordinate, specifically line 350 of grob.cc as 
of 9e605a1bb6644d89cf110f20cb6d46bb0339fca7).  I like Keith's idea of 
tentative and permanent.  The model I'm thinking of is:

--) LilyPond evaluates all callbacks as permanent unless tentative is 
explicitly asked for.
--) Permanent properties are stashed in a cache that cannot be erased.
--) Tentative properties may (i.e. heights) or may not (i.e. color) be cached 
depending on how we choose to optimize LilyPond.  It is the responsibility of 
the coder, if she wants an uncached property, to wipe the cache.
--) Most importantly, all notions of pure callbacks disappear.  It is the job 
of functions to police themselves.  For example, a function Grob::has_full_x 
can be created to determine if a grob has an X position with respect to the 
system.  It'd check to see if the dim_cache_[X_AXIS].offset_ of a Paper_column 
pointing to a real location in memory.  The answer will be false if 
System::break_into_pieces has been called and true otherwise.  Then, we could 
do something like:

MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, height, 1, 2, );
SCM
Slur::height (SCM smob, SCM begscm, SCM endscm)
{
  Grob *me = unsmob_grob (smob);
  if (me-has_full_x ())
return permanent_height (me);

  int beg = robust_scm2int (begscm, 0);
  int end = robust_scm2int (endscm, 0);
  return tentative_height (me, beg, end);
}

This'd allow to entirely get rid of all the pure callback lists as well as 
unpure-pure-containers.

--) Both permanent and tentative properties can result in calls to 
set_property, set_object, translate_axis, and suicide.  All setters should be 
used sparingly and internally to cache values (like we do for minimum 
translations, for example).  We can police this via warnings and errors.

Cheers,
MS

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread David Kastrup
m...@mikesolomon.org m...@mikesolomon.org writes:

 On 29 sept. 2012, at 19:54, m...@mikesolomon.org wrote:

 On 29 sept. 2012, at 19:53, Keith OHara k-ohara5...@oco.net
 wrote:
 
 On Sat, 29 Sep 2012 10:30:32 -0700, m...@mikesolomon.org
 m...@mikesolomon.org wrote:
 
 
 The way you're using tentative is almost exactly how
 pure properties are used in LilyPond.

 Specifically, 'pure-height being the estimated vertical extent
 before line-breaking, while 'height is its extent after
 line-breaking.
 
 If there are distinct properties to describe the position at
 different stages, then each property can be evaluated just
 once (as HanWen suggested, and as Mike agreed 100%).

 More thinking. I'm not enthusiastic about stages - it is a top down
 approach that locks us into certain points of evaluation. What if we
 decided to add or get rid of a stage? Would we need to create things
 like unpure-pure-containers for various stages? What qualifies as a
 stage?

Dependencies, I should guess.  A stage is where we break circular
dependencies.

-- 
David Kastrup


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread David Kastrup
David Kastrup d...@gnu.org writes:

 m...@mikesolomon.org m...@mikesolomon.org writes:

 On 29 sept. 2012, at 19:54, m...@mikesolomon.org wrote:

 On 29 sept. 2012, at 19:53, Keith OHara k-ohara5...@oco.net
 wrote:
 
 On Sat, 29 Sep 2012 10:30:32 -0700, m...@mikesolomon.org
 m...@mikesolomon.org wrote:
 
 
 The way you're using tentative is almost exactly how
 pure properties are used in LilyPond.

 Specifically, 'pure-height being the estimated vertical extent
 before line-breaking, while 'height is its extent after
 line-breaking.
 
 If there are distinct properties to describe the position at
 different stages, then each property can be evaluated just
 once (as HanWen suggested, and as Mike agreed 100%).

 More thinking. I'm not enthusiastic about stages - it is a top down
 approach that locks us into certain points of evaluation. What if we
 decided to add or get rid of a stage? Would we need to create things
 like unpure-pure-containers for various stages? What qualifies as a
 stage?

 Dependencies, I should guess.  A stage is where we break circular
 dependencies.

Basically, a grob says I want to have this and that information for
making my positioning and LilyPond says You can't get it right now.
Then the grob says ok, I'll do a tentative positioning, and LilyPond
will come back with more information later and ask again.

Now the problem here is when we are getting oscillation or even just a
converging process.  If there is convergence involved, we are better off
calculation the _relation_ between the positionings.  In a linear
optimization problem, those define the surface plane of a simplex (which
has possible solutions inside, impossible solutions outside, and where
we are looking for the furthest distance from 0 as the goal of
optimization) as a constraint.  Intersecting with other
surfaces/constraints gives us the total solution space, and travelling
outside along its edges (which are the intersection of two planes) moves
us to the optimal solution.

Doing this iteratively means jumping around on the inside of the
simplex.  Each jump may be quite faster than determining the active
boundaries of the simplex, but of course the simplex method focuses on
_those_ pairings of parameters/positioning which are actually valid
tradeoffs.  And since it is an efficient method, it does not get
confused when the heuristics go wrong.

-- 
David Kastrup


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread m...@mikesolomon.org

On 30 sept. 2012, at 11:39, David Kastrup d...@gnu.org wrote:

 David Kastrup d...@gnu.org writes:
 
 m...@mikesolomon.org m...@mikesolomon.org writes:
 
 On 29 sept. 2012, at 19:54, m...@mikesolomon.org wrote:
 
On 29 sept. 2012, at 19:53, Keith OHara k-ohara5...@oco.net
wrote:
 
On Sat, 29 Sep 2012 10:30:32 -0700, m...@mikesolomon.org
m...@mikesolomon.org wrote:
 
 
The way you're using tentative is almost exactly how
pure properties are used in LilyPond.
 
Specifically, 'pure-height being the estimated vertical extent
before line-breaking, while 'height is its extent after
line-breaking.
 
If there are distinct properties to describe the position at
different stages, then each property can be evaluated just
once (as HanWen suggested, and as Mike agreed 100%).
 
 More thinking. I'm not enthusiastic about stages - it is a top down
 approach that locks us into certain points of evaluation. What if we
 decided to add or get rid of a stage? Would we need to create things
 like unpure-pure-containers for various stages? What qualifies as a
 stage?
 
 Dependencies, I should guess.  A stage is where we break circular
 dependencies.
 
 Basically, a grob says I want to have this and that information for
 making my positioning and LilyPond says You can't get it right now.
 Then the grob says ok, I'll do a tentative positioning, and LilyPond
 will come back with more information later and ask again.
 
 Now the problem here is when we are getting oscillation or even just a
 converging process.  If there is convergence involved, we are better off
 calculation the _relation_ between the positionings.  In a linear
 optimization problem, those define the surface plane of a simplex (which
 has possible solutions inside, impossible solutions outside, and where
 we are looking for the furthest distance from 0 as the goal of
 optimization) as a constraint.  Intersecting with other
 surfaces/constraints gives us the total solution space, and travelling
 outside along its edges (which are the intersection of two planes) moves
 us to the optimal solution.
 
 Doing this iteratively means jumping around on the inside of the
 simplex.  Each jump may be quite faster than determining the active
 boundaries of the simplex, but of course the simplex method focuses on
 _those_ pairings of parameters/positioning which are actually valid
 tradeoffs.  And since it is an efficient method, it does not get
 confused when the heuristics go wrong.

I'm not completely sure that I follow what you're saying (I don't know anything 
about the internals of the simplex method) but if you mean that recalculating 
tentative property values may result in a series of values that doesn't 
converge towards anything, then yes, you are right.

Most tentative properties will likely have convergent behavior, meaning that as 
more information becomes available, they'll get closer and closer to actual 
properties.  However, there is no systematic way to enforce this.  I've spent a 
lot of time thinking about making linear programming possible in LilyPond and 
have come to the conclusion that aspects of LilyPond's logic make it such that 
decisions are not linear.  The classic is:

--) We do outside staff positioning, at which point element A gets shifted up.
--) Element B, not being able to be placed between element A and the staff, 
gets placed above element A.
--) We redo outside staff positioning after cross-staff-grobs' vertical 
skylines are calculated.
--) Element A is now shifted higher up.
--) Element B is now able to be stashed between element A and the staff.

That is a leap in heights (we go from having element B in the skyline to not).  
So discussing convergence is really difficult - I think the best we can do are 
heuristics to decide when to settle on ok-enough values.

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread Janek Warchoł
Hi,

interesting discussion, i learn a lot.

On Sun, Sep 30, 2012 at 11:39 AM, David Kastrup d...@gnu.org wrote:
 Basically, a grob says I want to have this and that information for
 making my positioning and LilyPond says You can't get it right now.
 Then the grob says ok, I'll do a tentative positioning, and LilyPond
 will come back with more information later and ask again.

I have no idea whether there are any not-very-experienced developers
(or users) following this thread, but if they are, i'm sure this is a
very nice and helpful explanation for them :)

cheers,
Janek

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread m...@mikesolomon.org

On 30 sept. 2012, at 14:16, Janek Warchoł janek.lilyp...@gmail.com wrote:

 Hi,
 
 interesting discussion, i learn a lot.
 
 On Sun, Sep 30, 2012 at 11:39 AM, David Kastrup d...@gnu.org wrote:
 Basically, a grob says I want to have this and that information for
 making my positioning and LilyPond says You can't get it right now.
 Then the grob says ok, I'll do a tentative positioning, and LilyPond
 will come back with more information later and ask again.
 
 I have no idea whether there are any not-very-experienced developers
 (or users) following this thread, but if they are, i'm sure this is a
 very nice and helpful explanation for them :)
 
 cheers,
 Janek

Just to clarify things for anyone following the thread: this is not currently 
how LilyPond works, but I'm assuming what you're proposing is a suggestion for 
a model.

It's an interesting idea for grobs to ping a sort of central hive (LilyPond 
in your text above) to know what information they can access and when.  This'd 
require a major change to the architecture - currently, grobs specify in their 
request whether they want tentative or permanent information via the use of 
functions like Grob::pure_relative_y_coordinate versus 
Grob::relative_coordinate.  I'm worried about having a sort of centralized 
brain that tells grobs what they can and can't know - sounds Kafka-esque.  I 
like the decentralized model where grobs, via their callbacks, self-police for 
what information they need from other grobs and when it's ok to get it.

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread David Kastrup
m...@mikesolomon.org m...@mikesolomon.org writes:

 On 30 sept. 2012, at 14:16, Janek Warchoł janek.lilyp...@gmail.com wrote:

 Hi,
 
 interesting discussion, i learn a lot.
 
 On Sun, Sep 30, 2012 at 11:39 AM, David Kastrup d...@gnu.org wrote:
 Basically, a grob says I want to have this and that information for
 making my positioning and LilyPond says You can't get it right now.
 Then the grob says ok, I'll do a tentative positioning, and LilyPond
 will come back with more information later and ask again.

 Just to clarify things for anyone following the thread: this is not
 currently how LilyPond works, but I'm assuming what you're proposing
 is a suggestion for a model.

 It's an interesting idea for grobs to ping a sort of central hive
 (LilyPond in your text above) to know what information they can
 access and when.  This'd require a major change to the architecture -
 currently, grobs specify in their request whether they want tentative
 or permanent information via the use of functions like
 Grob::pure_relative_y_coordinate versus Grob::relative_coordinate.
 I'm worried about having a sort of centralized brain that tells grobs
 what they can and can't know - sounds Kafka-esque.  I like the
 decentralized model where grobs, via their callbacks, self-police for
 what information they need from other grobs and when it's ok to get
 it.

I have my doubts that you can do a sensible circular dependency
resolution strategy in a purely local manner.  In fact, the current
pure/unpure distinction is based on before/after linebreaking, a
centralized operation.

-- 
David Kastrup

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread m...@mikesolomon.org

On 30 sept. 2012, at 14:29, David Kastrup d...@gnu.org wrote:

 m...@mikesolomon.org m...@mikesolomon.org writes:
 
 On 30 sept. 2012, at 14:16, Janek Warchoł janek.lilyp...@gmail.com wrote:
 
 Hi,
 
 interesting discussion, i learn a lot.
 
 On Sun, Sep 30, 2012 at 11:39 AM, David Kastrup d...@gnu.org wrote:
 Basically, a grob says I want to have this and that information for
 making my positioning and LilyPond says You can't get it right now.
 Then the grob says ok, I'll do a tentative positioning, and LilyPond
 will come back with more information later and ask again.
 
 Just to clarify things for anyone following the thread: this is not
 currently how LilyPond works, but I'm assuming what you're proposing
 is a suggestion for a model.
 
 It's an interesting idea for grobs to ping a sort of central hive
 (LilyPond in your text above) to know what information they can
 access and when.  This'd require a major change to the architecture -
 currently, grobs specify in their request whether they want tentative
 or permanent information via the use of functions like
 Grob::pure_relative_y_coordinate versus Grob::relative_coordinate.
 I'm worried about having a sort of centralized brain that tells grobs
 what they can and can't know - sounds Kafka-esque.  I like the
 decentralized model where grobs, via their callbacks, self-police for
 what information they need from other grobs and when it's ok to get
 it.
 
 I have my doubts that you can do a sensible circular dependency
 resolution strategy in a purely local manner.  In fact, the current
 pure/unpure distinction is based on before/after linebreaking, a
 centralized operation.
 

It is true that line-breaking is a centralized option based on what the 
toplevel-book-handler is, but it should be as lightweight as possible.  I think 
that the smaller we can keep paper-book.cc and paper-score.cc, the better.  
I've been saying this for a couple years, but I'd prefer for Book and 
PaperScore to be grobs so that even they could use the callback model.  At that 
point, line breaking could just be controlled by callbacks.  This would also 
pave the way for better Prob / Grob integration - it is currently difficult to 
get toplevel markups and systems to play nicely because they are outside of 
this callback model.

The current pure/unpure distinction is based on what people ask for and when.  
Pure offsets and extents are used until the dim_cache_ is filled, at which 
point real offsets and extents are used.  As a convention this is before and 
after line breaking, but there are places where pure properties are requested 
after linebreaking and non-pure properties are requested before.

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-30 Thread David Kastrup
m...@mikesolomon.org m...@mikesolomon.org writes:

 It is true that line-breaking is a centralized option based on what
 the toplevel-book-handler is, but it should be as lightweight as
 possible.  I think that the smaller we can keep paper-book.cc and
 paper-score.cc, the better.  I've been saying this for a couple years,
 but I'd prefer for Book and PaperScore to be grobs so that even they
 could use the callback model.  At that point, line breaking could just
 be controlled by callbacks.

Distributing algorithms in that manner without central
control/arbitration means O(n^2) complexity at least.  It tends to lend
itself better to parallelizing, but when your average system does not
have thousands of processors, that advantage remains very limited.

Of course, a half-baked not-well-understood global hackery touching
stuff in lots of indiscriminate places is not what this is about.  The
interdependencies need to be stated as local relations, of course.  It
is just that the resolution is to be done globally.  Of course this
necessitates that the dependencies are formulated in a _systematic_
manner and in the same way everywhere, using well-crafted
interfaces/ways of specification rather than in willy-nilly adhoc
jumbles of callbacks.

-- 
David Kastrup


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread Han-Wen Nienhuys
On Wed, Sep 26, 2012 at 10:40 PM, David Kastrup d...@gnu.org wrote:
 Han-Wen Nienhuys hanw...@gmail.com writes:

 In order to do cache invalidation, you will have to construct the
 reverse graph. If A.x depends on B.y, now A points to B. For proper
 cache invalidation, if B.y changes, then A.x becomes invalid. This
 means that A has to store a back-reference to B. Hence all pointers
 have to be stored both ways (including inverting 1-N relations into
 N-1 relations), and the both-way links have to be rewritten correctly
 during line breaking.

 You can assign a generational count to properties.  If the generational
 count of any dependency is higher than that of the dependent property,
 it needs to get regenerated.  As long as the dependencies don't get lost
 (and we use arbitrary size integers, resetting for each session), this
 should be pretty straightforward and not require backwards links.  It
 just requires double-checking your dependencies for change.

But since the dependencies are also properties that could be
invalidated, you'd have to recurse , so each property access would
potentially expand into an almost infinite number of get_property
calls.

I think it is a much clearer abstraction to decide that each property
can only be evaluated once, and that everything should be driven by
callbacks. In fact, one thing I would suggest looking at is removing
{before,after}_line_breaking which breaks this model somewhat.

-- 
Han-Wen Nienhuys - han...@xs4all.nl - http://www.xs4all.nl/~hanwen

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread m...@mikesolomon.org

On 29 sept. 2012, at 10:59, Han-Wen Nienhuys hanw...@gmail.com wrote:

 On Wed, Sep 26, 2012 at 10:40 PM, David Kastrup d...@gnu.org wrote:
 Han-Wen Nienhuys hanw...@gmail.com writes:
 
 In order to do cache invalidation, you will have to construct the
 reverse graph. If A.x depends on B.y, now A points to B. For proper
 cache invalidation, if B.y changes, then A.x becomes invalid. This
 means that A has to store a back-reference to B. Hence all pointers
 have to be stored both ways (including inverting 1-N relations into
 N-1 relations), and the both-way links have to be rewritten correctly
 during line breaking.
 
 You can assign a generational count to properties.  If the generational
 count of any dependency is higher than that of the dependent property,
 it needs to get regenerated.  As long as the dependencies don't get lost
 (and we use arbitrary size integers, resetting for each session), this
 should be pretty straightforward and not require backwards links.  It
 just requires double-checking your dependencies for change.
 
 But since the dependencies are also properties that could be
 invalidated, you'd have to recurse , so each property access would
 potentially expand into an almost infinite number of get_property
 calls.
 
 I think it is a much clearer abstraction to decide that each property
 can only be evaluated once, and that everything should be driven by
 callbacks. In fact, one thing I would suggest looking at is removing
 {before,after}_line_breaking which breaks this model somewhat.
 

I agree 110%.  The only corollary I'd add, which comes from my recent work, is 
that pure properties be re-evaluatable all the time (meaning that one should be 
allowed to arbitrarily flush the cache) to store better and better 
approximations of things.  For example, if one wants the pure heights of 
cross-staff beamed stems, the approximation one can get before line breaking is 
worse than that which one can get after.  After line breaking, one could 
theoretically run beam quanting using the pure heights and actual X positions, 
whereas before, these X positions don't exist.  So, pure properties in LilyPond 
become progressively more accurate as they go downstream, and once someone is 
positive that all the information is there to evaluate the actual property, 
get_property (or get_extent or whatever) is called.

I agree about (before,after)_line_breaking.

The single biggest offenders of this model, in my mind, are to be found in the 
engraver stage.  More specifically, I think that if we can get rid of the 
Tweak_engraver such that every property is initialized via the Grob::Grob (SCM 
basicprops) constructor (like in engraver.cc, for example) and not via 
set_property at the engraver stage, everything else will be easier.

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread Han-Wen Nienhuys
On Sat, Sep 29, 2012 at 11:35 AM, m...@mikesolomon.org
m...@mikesolomon.org wrote:

 On 29 sept. 2012, at 10:59, Han-Wen Nienhuys hanw...@gmail.com wrote:

 On Wed, Sep 26, 2012 at 10:40 PM, David Kastrup d...@gnu.org wrote:
 Han-Wen Nienhuys hanw...@gmail.com writes:

 In order to do cache invalidation, you will have to construct the
 reverse graph. If A.x depends on B.y, now A points to B. For proper
 cache invalidation, if B.y changes, then A.x becomes invalid. This
 means that A has to store a back-reference to B. Hence all pointers
 have to be stored both ways (including inverting 1-N relations into
 N-1 relations), and the both-way links have to be rewritten correctly
 during line breaking.

 You can assign a generational count to properties.  If the generational
 count of any dependency is higher than that of the dependent property,
 it needs to get regenerated.  As long as the dependencies don't get lost
 (and we use arbitrary size integers, resetting for each session), this
 should be pretty straightforward and not require backwards links.  It
 just requires double-checking your dependencies for change.

 But since the dependencies are also properties that could be
 invalidated, you'd have to recurse , so each property access would
 potentially expand into an almost infinite number of get_property
 calls.

 I think it is a much clearer abstraction to decide that each property
 can only be evaluated once, and that everything should be driven by
 callbacks. In fact, one thing I would suggest looking at is removing
 {before,after}_line_breaking which breaks this model somewhat.


 I agree 110%.  The only corollary I'd add, which comes from my recent work, 
 is that pure properties be re-evaluatable all the time (meaning that one 
 should be allowed to arbitrarily flush the cache) to store better and better 
 approximations of things.  For example, if one wants the pure heights of 
 cross-staff beamed stems, the approximation one can get before line breaking 
 is worse than that which one can get after.  After line breaking, one could 
 theoretically run beam quanting using the pure heights and actual X 
 positions, whereas before, these X positions don't exist.  So, pure 
 properties in LilyPond become progressively more accurate as they go 
 downstream, and once someone is positive that all the information is there to 
 evaluate the actual property, get_property (or get_extent or whatever) is 
 called.

pure-properties are misnamed. If they are always are recomputed, they
should be called methods or even pure functions and not properties.

Of course, if there is a cache, you have to think about a simple and
rigorous scheme to know when the cache can be thrown away; the real
problem is about caching data based on other grobs, since invalidation
implies a lot more of pointer juggling

 I agree about (before,after)_line_breaking.

 The single biggest offenders of this model, in my mind, are to be found in 
 the engraver stage.  More specifically, I think that if we can get rid of the 
 Tweak_engraver such that every property is initialized via the Grob::Grob 
 (SCM basicprops) constructor (like in engraver.cc, for example) and not via 
 set_property at the engraver stage, everything else will be easier.

You could make a separate routine that prepends sym/value pairs onto
the immutable list; I'm not certain that this would intrinsically buy
you anything, though.

-- 
Han-Wen Nienhuys - han...@xs4all.nl - http://www.xs4all.nl/~hanwen

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread Keith OHara
Han-Wen Nienhuys hanwenn at gmail.com writes:

 I think it is a much clearer abstraction to decide that each property
 can only be evaluated once, and that everything should be driven by
 callbacks. In fact, one thing I would suggest looking at is removing
 {before,after}_line_breaking which breaks this model somewhat.
 

Should that apply to properties that describe positioning of objects? 
Often LilyPond needs tentative positions for objects.

For example, when setting note-spacing constraints, the separation of staves is 
taken to be large enough that items do not interfere between staves.  The 
widths 
of text items are ignored, based on their extra-spacing-* properties.  Rests 
avoiding beams have a tentative position.  Some repeated accidentals on tied 
notes exist, but may disappear.

The note-spacing code uses skylines, which depend on positions for the enclosed 
objects, and wants tentative positions appropriate to this phase of layout.

One way to organize these phases of layout is by registering callbacks to be 
performed (or maybe in future reset) between phases: 
before/after-line-breaking. 

The alternative organizational method in the code today is religious 
observance: 
for it is written, On the day of Note-Spacing, thou shalt avert thine eyes from 
the stems, for stems keep the company of beams, and beams are impure, thus to 
seek knowledge of the tip of a stem would be impure.



___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread m...@mikesolomon.org

On 29 sept. 2012, at 18:34, Keith OHara k-ohara5...@oco.net wrote:

 Han-Wen Nienhuys hanwenn at gmail.com writes:
 
 I think it is a much clearer abstraction to decide that each property
 can only be evaluated once, and that everything should be driven by
 callbacks. In fact, one thing I would suggest looking at is removing
 {before,after}_line_breaking which breaks this model somewhat.
 
 
 Should that apply to properties that describe positioning of objects? 
 Often LilyPond needs tentative positions for objects.
 
 For example, when setting note-spacing constraints, the separation of staves 
 is 
 taken to be large enough that items do not interfere between staves.  The 
 widths 
 of text items are ignored, based on their extra-spacing-* properties.  Rests 
 avoiding beams have a tentative position.  Some repeated accidentals on tied 
 notes exist, but may disappear.
 
 The note-spacing code uses skylines, which depend on positions for the 
 enclosed 
 objects, and wants tentative positions appropriate to this phase of layout.
 

The way you're using tentative is almost exactly how pure properties are used 
in LilyPond.  The only example that doesn't work that way is the accidental 
one, which I believe is a bug in LilyPond - I want to say that the space is 
reserved even after the accidental is killed off (I'd have to verify that).

 One way to organize these phases of layout is by registering callbacks to be 
 performed (or maybe in future reset) between phases: 
 before/after-line-breaking. 
 
 The alternative organizational method in the code today is religious 
 observance: 
 for it is written, On the day of Note-Spacing, thou shalt avert thine eyes 
 from 
 the stems, for stems keep the company of beams, and beams are impure, thus to 
 seek knowledge of the tip of a stem would be impure.
 
 

I am a fan of the second over the first.  I think all property look-ups should 
be pure until one is absolutely certain that the property won't change, at 
which point the unpure value should be cached and returned for both pure and 
unpure queries (impure sounds too religious, thus unpure).  The pure properties 
need not always return the same value - they can get closer and closer to the 
unpure as more and more information gets determined.  Cached pure values for 
Items can be flushed at any point if someone wants a potentially better 
approximation, at which point the calculation would be redone and the cache 
restocked.

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread Keith OHara

On Sat, 29 Sep 2012 10:30:32 -0700, m...@mikesolomon.org m...@mikesolomon.org 
wrote:



The way you're using tentative is almost exactly how pure properties are used 
in LilyPond.


Specifically, 'pure-height being the estimated vertical extent before 
line-breaking, while 'height is its extent after line-breaking.

If there are distinct properties to describe the position at different stages, 
then each property can be evaluated just once (as HanWen suggested, and as Mike 
agreed 100%).



I think all property look-ups should be pure until one is absolutely certain 
that the property won't change, at which point the unpure value should be 
cached and returned for both pure and unpure queries (impure sounds too 
religious, thus unpure).  The pure properties need not always return the same 
value - they can get closer and closer to the unpure as more and more 
information gets determined.


This is the opposite approach, where some properties are not permanent at 
all.  The word 'tentative' is a better label than 'pure', because pure functions would 
always return the same value given the same arguments.


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-29 Thread m...@mikesolomon.org

On 29 sept. 2012, at 19:53, Keith OHara k-ohara5...@oco.net wrote:

 On Sat, 29 Sep 2012 10:30:32 -0700, m...@mikesolomon.org 
 m...@mikesolomon.org wrote:
 
 
 The way you're using tentative is almost exactly how pure properties are 
 used in LilyPond.
 
 Specifically, 'pure-height being the estimated vertical extent before 
 line-breaking, while 'height is its extent after line-breaking.
 
 If there are distinct properties to describe the position at different 
 stages, then each property can be evaluated just once (as HanWen suggested, 
 and as Mike agreed 100%).
 
 
 I think all property look-ups should be pure until one is absolutely certain 
 that the property won't change, at which point the unpure value should be 
 cached and returned for both pure and unpure queries (impure sounds too 
 religious, thus unpure).  The pure properties need not always return the 
 same value - they can get closer and closer to the unpure as more and more 
 information gets determined.
 
 This is the opposite approach, where some properties are not permanent at 
 all.  The word 'tentative' is a better label than 'pure', because pure 
 functions would always return the same value given the same arguments.
 

True dat.  Someone wanna do a perl one-line replace subbing in tenative for 
pure and post the patch? :-)

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and, outside-staff-priority

2012-09-27 Thread Mats Bengtsson


On 09/27/2012 08:36 AM, lilypond-devel-requ...@gnu.org wrote:

A typical example of this is NoteCollision of N NoteColumns. The
NoteColumns have to all move in a coordinated way, and the easiest way
is to have a function (with local variables) that determines what has
to happen. You might get around it, by having a bunch of properties
instead, but you'd still have to store those somewhere, ie in the
NoteCollision grob.

Not necessarily, it'd just make computation time longer.  If each note column 
had other concurrent note columns in its side-position-elements grob-array and 
we kept the same function from note-column.cc but rewrote it such that instead 
of translating axes by values it returned the offset value for a given note 
column, the function would be called N times for N note columns.  It's a 
tradeoff between efficiency and consistency - I'm not sure which one is better, 
but I'm sure it's possible to eliminate the NoteCollision grob at the cost of 
redundancy.

The main issue is which solution is easiest to maintain and extend for 
future LilyPond developers. To me, it seems like it's easier to get an 
overview of an implementation where a single centralized entity collects 
all the necessary information and takes the decisions, compared to a 
decentralized implementation where the information and logic is 
distributed over a number of different objects. Admittedly, we already 
have many examples of the latter strategy in the current LilyPond 
implementation and I'm sure that it had contributed to the flexibility 
and power of the general framework. Whatever solution is selected, 
please don't forget to keep maintainability and generality as the main 
guiding principle. This seems to be sometimes missing from the 
discussions on the mailing list.


   /Mats

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and, outside-staff-priority

2012-09-27 Thread David Kastrup
Mats Bengtsson mats.bengts...@ee.kth.se writes:

 On 09/27/2012 08:36 AM, lilypond-devel-requ...@gnu.org wrote:
 A typical example of this is NoteCollision of N NoteColumns. The
 NoteColumns have to all move in a coordinated way, and the easiest way
 is to have a function (with local variables) that determines what has
 to happen. You might get around it, by having a bunch of properties
 instead, but you'd still have to store those somewhere, ie in the
 NoteCollision grob.
 Not necessarily, it'd just make computation time longer.  If each
 note column had other concurrent note columns in its
 side-position-elements grob-array and we kept the same function from
 note-column.cc but rewrote it such that instead of translating axes
 by values it returned the offset value for a given note column, the
 function would be called N times for N note columns.  It's a
 tradeoff between efficiency and consistency - I'm not sure which one
 is better, but I'm sure it's possible to eliminate the NoteCollision
 grob at the cost of redundancy.

 The main issue is which solution is easiest to maintain and extend for
 future LilyPond developers. To me, it seems like it's easier to get an
 overview of an implementation where a single centralized entity
 collects all the necessary information and takes the decisions,
 compared to a decentralized implementation where the information and
 logic is distributed over a number of different objects.

There is not actually much of a difference regarding the maintenance
level here as long as the information and logic is not distributed over
a number of different _code_ passages.

What we need is to do x with y, you call z.  z may be an API function,
it may be a method of y.  What we don't need is to do x with y, you
fiddle with y.z, try keeping w in sync by adding y.v here, and if there
is a w.g that has a common q with y.t, you need to make sure that the
pure callback of q.r is updated along with ...

You get the drift.

-- 
David Kastrup


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread David Kastrup
m...@mikesolomon.org m...@mikesolomon.org writes:

 As was the case in a few of my previous projects, before I start
 something new I make architecture changes that facilitate my work.
 Working on 2801, I've realized that any multi-pass algorithm for the
 spacing of grobs is difficult because results of callback calculations
 are always cached.  So triggering callbacks a second time is, in the
 current architecture, impractical and requires a fair bit of kludgery.

[...]

 performance is uncertain, but it'd probably require more optimization
 in skyline.cc and/or caching of skylines.

It sounds to me like you consider caching a bad idea so you want to
remove it, and to make this removal feasible you think you will be
required to do caching.

It sounds to me like it would make more sense that we improve our cache
invalidation strategy where it goes wrong rather than shifting the
problem around in increasingly complex manners.

-- 
David Kastrup


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread m...@mikesolomon.org

On 26 sept. 2012, at 13:39, David Kastrup d...@gnu.org wrote:

 m...@mikesolomon.org m...@mikesolomon.org writes:
 
 As was the case in a few of my previous projects, before I start
 something new I make architecture changes that facilitate my work.
 Working on 2801, I've realized that any multi-pass algorithm for the
 spacing of grobs is difficult because results of callback calculations
 are always cached.  So triggering callbacks a second time is, in the
 current architecture, impractical and requires a fair bit of kludgery.
 
 [...]
 
 performance is uncertain, but it'd probably require more optimization
 in skyline.cc and/or caching of skylines.
 
 It sounds to me like you consider caching a bad idea so you want to
 remove it, and to make this removal feasible you think you will be
 required to do caching.
 

You're right - I explained myself poorly.

The caching of skylines would come from similar content.  i.e. if Skyline X == 
Skyline A and Skyline Y == Skyline B and we have already measured the distance 
between A and B, use that as the distance between X and Y.  Here, equality is 
defined as having buildings with the same start_, end_, slope_ and y_offset_.  
So it's a different type of caching than the caching of grob properties.

 It sounds to me like it would make more sense that we improve our cache
 invalidation strategy where it goes wrong

There is currently no cache invalidation strategy.

 rather than shifting the
 problem around in increasingly complex manners.
 

There is currently no way in LilyPond to trace what properties depend on what 
properties.  So if we want to invalidate the cache of property X, it is very 
difficult to invalidate the caches of properties that depend on it.  This is 
made considerably easier if side-positioning is streamlined.  When a grob X's 
side position changes for a given axis, iterate over its side-position elements 
and recalculate their offsets.  That's one of the main reasons I want to make 
this change.

I think the change decreases complexity as it makes LilyPond more predictable 
and boring - objects side position based off of other objects and that's it.  
No need for side-position, parents and outside-staff-priority.

Cheers,
MS


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread David Kastrup
m...@mikesolomon.org m...@mikesolomon.org writes:

 On 26 sept. 2012, at 13:39, David Kastrup d...@gnu.org wrote:

 It sounds to me like it would make more sense that we improve our
 cache invalidation strategy where it goes wrong

 There is currently no cache invalidation strategy.

Sounds like we found the place where it goes wrong.

 rather than shifting the
 problem around in increasingly complex manners.

 There is currently no way in LilyPond to trace what properties depend
 on what properties.  So if we want to invalidate the cache of property
 X, it is very difficult to invalidate the caches of properties that
 depend on it.  This is made considerably easier if side-positioning is
 streamlined.  When a grob X's side position changes for a given axis,
 iterate over its side-position elements and recalculate their offsets.
 That's one of the main reasons I want to make this change.

What we need to arrive at is a situation where somebody without a clue
starting to write stuff will more likely than not get a whole lot of
things working right without realizing it, rather than getting a whole
lot of things working wrong without realizing it.

Which apparently is what is happening to Marc right now.  He is working
on a simple problem, and it fails for complex reasons.  And that means
the current design of our backend is bad.

Conceptually simple problems need to map to conceptually simple
solutions.  If they don't, our APIs suck.

 I think the change decreases complexity as it makes LilyPond more
 predictable and boring - objects side position based off of other
 objects and that's it.  No need for side-position, parents and
 outside-staff-priority.

parents are used for a _lot_ of positioning, and for example the
determination of a common grob parent is an _efficient_ operation.  So
it might make sense to solve the problem you are thinking of via
artificial/grouping parents.  I can't vouch for it, obviously, as the
backend is mostly opaque to me right now.  But it is a possibility.

-- 
David Kastrup

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread m...@mikesolomon.org

On 26 sept. 2012, at 14:13, David Kastrup d...@gnu.org wrote:

 What we need to arrive at is a situation where somebody without a clue
 starting to write stuff will more likely than not get a whole lot of
 things working right without realizing it, rather than getting a whole
 lot of things working wrong without realizing it.

I completely agree.

 
 Which apparently is what is happening to Marc right now.  He is working
 on a simple problem, and it fails for complex reasons.  And that means
 the current design of our backend is bad.
 
 Conceptually simple problems need to map to conceptually simple
 solutions.  If they don't, our APIs suck.

We don't have APIs, which is a separate but equally problematic issue.

The conceptually simple problem is that grobs are positioned with respect to 
other grobs.  The conceptually simple solution is that grobs contain arrays of 
these other grobs and use them for the positioning.  That's why I want to make 
this change - it provides one-stop-shopping for positioning changes.

 
 I think the change decreases complexity as it makes LilyPond more
 predictable and boring - objects side position based off of other
 objects and that's it.  No need for side-position, parents and
 outside-staff-priority.
 
 parents are used for a _lot_ of positioning, and for example the
 determination of a common grob parent is an _efficient_ operation.  So
 it might make sense to solve the problem you are thinking of via
 artificial/grouping parents.

This is totally possible - you check the side-position-elements array, and if 
it has multiple members, you find their common refpoint.  This can go all the 
way up to System and VerticalAlignment, both of which will have 
side-position-element arrays of size 0 for both the X and Y axes.

Cheers,
MS
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread Francisco Vila
2012/9/26 m...@mikesolomon.org m...@mikesolomon.org:

 On 26 sept. 2012, at 14:13, David Kastrup d...@gnu.org wrote:
 Conceptually simple problems need to map to conceptually simple
 solutions.  If they don't, our APIs suck.

 We don't have APIs,

Sounds like we found the reason they suck.

Couldn't reseist, sorry.
-- 
Francisco Vila. Badajoz (Spain)
www.paconet.org , www.csmbadajoz.com

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread Joe Neeman
On Wed, Sep 26, 2012 at 4:15 AM, m...@mikesolomon.org
m...@mikesolomon.orgwrote:

 Hey all,

 As was the case in a few of my previous projects, before I start something
 new I make architecture changes that facilitate my work.  Working on 2801,
 I've realized that any multi-pass algorithm for the spacing of grobs is
 difficult because results of callback calculations are always cached.  So
 triggering callbacks a second time is, in the current architecture,
 impractical and requires a fair bit of kludgery.


Are you proposing not to cache callbacks at all? That sounds like a real
performance killer to me; we would probably end up re-typesetting each beam
hundreds of times.

By the way, the problem is not only in the caching of callbacks; there are
also many other places with side effects (calls to set_property,
translate_axis, suicide, etc).

To start this process, I'd like to expand the role of the
 side-position-interface significantly.  All grobs would use this interface
 for their X and Y offsets and they would have two different grob arrays for
 X and Y side-position-elements.  This would allow me to eliminate two
 things:

 1) parents.  Grobs are X/Y aligned to parents, but these parents could be
 elements in the side-position-elements array.  Functions like get_parent,
 which could be kept around for legacy reasons, could return the first
 element of these arrays for a given axis and raise a warning if there are
 multiple elements.


If you get rid of unique parents, what would X-offset mean? Would it be
relative to the System? The first element of side-position-elements array?

2) outside-staff-priority.  Saying a grob has outside staff priority is the
 same thing as saying that a grob has as its Y side support elements all
 inside-staff grobs plus all grobs with lower outside staff priority.  A
 callback creating the side-position-elements arrays could do the sorting
 that takes place in Axis_group_interface::skyline_spacing.


I think you'll have trouble making this fully callbacky/functional. For
example, we lay out the outside-staff grobs in a very particular order (the
left-to-right layout thing), which we don't know ahead of time. That is,
until we get _all_ of the outside-staff grobs together, we don't know which
grobs have to depend on which other grobs. That means it isn't really
possible for each grob to determine its own position; there needs to be one
grob that lays them out simultaneously. The same issue will come up, I
think in laying out Accidentals and VerticalAxisGroups.

At some point, I had grand plans to formalize this problem by having two
distinct kinds of properties: one that is user-overridable using callbacks,
and one that is internal and gets set by other grobs as a side-effect.
Everything would have explicit dependencies so that caches could be
invalidated. I even started to write a proof-of-concept in scala, but I
never finished it...


 The benefits of this are twofold:

 1) All work on a multi-pass algorithm could be done with respect to
 side-positioning, making it simpler to find bugs and modify code.


Not all positioning is side-positioning.

2) A call to translate_axis in avoid_outside_staff_collisions would be
 eliminated, and translate_axis is bad: it goes against the callback model
 at the core of LilyPond layout.


Agreed, but this is just one instance of translate_axis, which is just one
example of a side-effect in lilypond. Is your eventual goal to remove them
all?

The disadvantage is that this results in the construction of many more
 skylines (one for each call to
 Side_position_interface::skyline_side_position).  How this will impact
 performance is uncertain, but it'd probably require more optimization in
 skyline.cc and/or caching of skylines.


I would suggest that you completely ignore performance in favor of a clean
design. Performance can come later.

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread m...@mikesolomon.org
On 26 sept. 2012, at 17:38, Joe Neeman joenee...@gmail.com wrote:

 On Wed, Sep 26, 2012 at 4:15 AM, m...@mikesolomon.org m...@mikesolomon.org 
 wrote:
 Hey all,
 
 As was the case in a few of my previous projects, before I start something 
 new I make architecture changes that facilitate my work.  Working on 2801, 
 I've realized that any multi-pass algorithm for the spacing of grobs is 
 difficult because results of callback calculations are always cached.  So 
 triggering callbacks a second time is, in the current architecture, 
 impractical and requires a fair bit of kludgery.
 
 Are you proposing not to cache callbacks at all? That sounds like a real 
 performance killer to me; we would probably end up re-typesetting each beam 
 hundreds of times.

No, I'm proposing to find way to mark things as dirty so that they are 
recalculated.

 
 By the way, the problem is not only in the caching of callbacks; there are 
 also many other places with side effects (calls to set_property, 
 translate_axis, suicide, etc).
 

Exactly - the proposal here is to get rid of the call to translate_axis in 
axis-group-interface.cc, which is the biggest side effect in the code base in 
terms of the number and variety of grobs affected.  This'll make it easier to 
mark things as dirty so that multiple passes can happen.

 To start this process, I'd like to expand the role of the 
 side-position-interface significantly.  All grobs would use this interface 
 for their X and Y offsets and they would have two different grob arrays for X 
 and Y side-position-elements.  This would allow me to eliminate two things:
 
 1) parents.  Grobs are X/Y aligned to parents, but these parents could be 
 elements in the side-position-elements array.  Functions like get_parent, 
 which could be kept around for legacy reasons, could return the first element 
 of these arrays for a given axis and raise a warning if there are multiple 
 elements.
 
 If you get rid of unique parents, what would X-offset mean? Would it be 
 relative to the System? The first element of side-position-elements array? 
 

Relative to the skyline of the elements of the side-position-elements array.

 2) outside-staff-priority.  Saying a grob has outside staff priority is the 
 same thing as saying that a grob has as its Y side support elements all 
 inside-staff grobs plus all grobs with lower outside staff priority.  A 
 callback creating the side-position-elements arrays could do the sorting that 
 takes place in Axis_group_interface::skyline_spacing.
 
 I think you'll have trouble making this fully callbacky/functional. For 
 example, we lay out the outside-staff grobs in a very particular order (the 
 left-to-right layout thing), which we don't know ahead of time. That is, 
 until we get _all_ of the outside-staff grobs together, we don't know which 
 grobs have to depend on which other grobs. That means it isn't really 
 possible for each grob to determine its own position; there needs to be one 
 grob that lays them out simultaneously. The same issue will come up, I think 
 in laying out Accidentals and VerticalAxisGroups.
 

You're right that, in general, any time that a collecting grob does some type 
of organizing, this type of problem will come up.  However, there are ways 
around it.  For example, a grob can get the elements array from its vertical 
alignment or system, sort it from left to right, and trigger callbacks for all 
grobs to the left.  This avoids an overarching positioning grob while still 
achieving the same effect.

 At some point, I had grand plans to formalize this problem by having two 
 distinct kinds of properties: one that is user-overridable using callbacks, 
 and one that is internal and gets set by other grobs as a side-effect. 
 Everything would have explicit dependencies so that caches could be 
 invalidated. I even started to write a proof-of-concept in scala, but I never 
 finished it...

My thinking is that in order to make dependencies easier to handle and 
maintain, we need to reduce the number of side effects like translate_axis and 
set_property.

 
 
 The benefits of this are twofold:
 
 1) All work on a multi-pass algorithm could be done with respect to 
 side-positioning, making it simpler to find bugs and modify code.
 
 Not all positioning is side-positioning.

True - my goal is to see if all positioning can be articulated this way.  For 
example, ScriptColumn could have used translate_axis, but whoever wrote it made 
the good decision to use side-position-interface.  I think this grob could be 
eliminated entirely if all scripts in a script column had a function that 
calculated the side-position-elements grob-array using the sorting algorithm at 
the heart of script-column.cc.

Another way to think of this is that, in general, we'd try to eliminate grobs 
like NoteColumn, ScriptColumn, DynamicLineSpanner, etc that only do 
traffic-coppery.  Or, inversely, we'd say that positioning only happens via 
traffic-cop grobs 

Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread Han-Wen Nienhuys
On Wed, Sep 26, 2012 at 9:17 PM, m...@mikesolomon.org
m...@mikesolomon.org wrote:

 Another way to think of this is that, in general, we'd try to eliminate
 grobs like NoteColumn, ScriptColumn, DynamicLineSpanner, etc that only do
 traffic-coppery.  Or, inversely, we'd say that positioning only happens via
 traffic-cop grobs and no grob has the right to position itself.  But it
 seems like we need to pick one.

The reason for trafic-coppery is that for many positioning schemes,
there is no inherent order that can determine how two objects of the
same type (and hence, with the same set of callbacks) should be
typeset.

A typical example of this is NoteCollision of N NoteColumns. The
NoteColumns have to all move in a coordinated way, and the easiest way
is to have a function (with local variables) that determines what has
to happen. You might get around it, by having a bunch of properties
instead, but you'd still have to store those somewhere, ie in the
NoteCollision grob.

 Agreed, but this is just one instance of translate_axis, which is just one
 example of a side-effect in lilypond. Is your eventual goal to remove them
 all?


 Yes - that'd be great.  That'll make explicit dependencies a lot easier to
 handle - everything can be calculated in terms of callbacks.

I'm all for removing side effects, but you can pursue that separately
without trying to rewrite the backend.


 To keep the main thing the main thing, the goal is to be able to wipe
 certain caches, restore the original callbacks, and know that all grobs
 properties that depended on the restored property will also be recalculated.
 My idea is an idea for simplifying LilyPond so that this work is easier, but
 there is no point in doing that if it won't help reach that goal.  I'm open
 to any and all suggestions.

Have you thought this through?

In order to do cache invalidation, you will have to construct the
reverse graph. If A.x depends on B.y, now A points to B. For proper
cache invalidation, if B.y changes, then A.x becomes invalid. This
means that A has to store a back-reference to B. Hence all pointers
have to be stored both ways (including inverting 1-N relations into
N-1 relations), and the both-way links have to be rewritten correctly
during line breaking.

-- 
Han-Wen Nienhuys - han...@xs4all.nl - http://www.xs4all.nl/~hanwen

___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread Joe Neeman
On Wed, Sep 26, 2012 at 12:17 PM, m...@mikesolomon.org m...@mikesolomon.org
 wrote:

 On 26 sept. 2012, at 17:38, Joe Neeman joenee...@gmail.com wrote:

 On Wed, Sep 26, 2012 at 4:15 AM, m...@mikesolomon.org 
 m...@mikesolomon.org wrote:


 To start this process, I'd like to expand the role of the
 side-position-interface significantly.  All grobs would use this interface
 for their X and Y offsets and they would have two different grob arrays for
 X and Y side-position-elements.  This would allow me to eliminate two
 things:

 1) parents.  Grobs are X/Y aligned to parents, but these parents could be
 elements in the side-position-elements array.  Functions like get_parent,
 which could be kept around for legacy reasons, could return the first
 element of these arrays for a given axis and raise a warning if there are
 multiple elements.


 If you get rid of unique parents, what would X-offset mean? Would it be
 relative to the System? The first element of side-position-elements array?


 Relative to the skyline of the elements of the side-position-elements
 array.


If you want to construct one skyline for all the elements in that array,
you need some way of finding their offsets relative to each other. How do
you propose to do that without some notion of a common refpoint?

2) outside-staff-priority.  Saying a grob has outside staff priority is the
 same thing as saying that a grob has as its Y side support elements all
 inside-staff grobs plus all grobs with lower outside staff priority.  A
 callback creating the side-position-elements arrays could do the sorting
 that takes place in Axis_group_interface::skyline_spacing.


 I think you'll have trouble making this fully callbacky/functional. For
 example, we lay out the outside-staff grobs in a very particular order (the
 left-to-right layout thing), which we don't know ahead of time. That is,
 until we get _all_ of the outside-staff grobs together, we don't know which
 grobs have to depend on which other grobs. That means it isn't really
 possible for each grob to determine its own position; there needs to be one
 grob that lays them out simultaneously. The same issue will come up, I
 think in laying out Accidentals and VerticalAxisGroups.


 You're right that, in general, any time that a collecting grob does some
 type of organizing, this type of problem will come up.  However, there are
 ways around it.  For example, a grob can get the elements array from its
 vertical alignment or system, sort it from left to right, and trigger
 callbacks for all grobs to the left.  This avoids an overarching
 positioning grob while still achieving the same effect.


For outside-staff positioning, I think you could achieve the same effect.
For something more complicated, like TieColumn or AccidentalPlacement, I
think it would be much more difficult (and more obfuscated) to do it with
callbacks.



 The benefits of this are twofold:

 1) All work on a multi-pass algorithm could be done with respect to
 side-positioning, making it simpler to find bugs and modify code.


 Not all positioning is side-positioning.


 True - my goal is to see if all positioning can be articulated this way.
  For example, ScriptColumn could have used translate_axis, but whoever
 wrote it made the good decision to use side-position-interface.  I think
 this grob could be eliminated entirely if all scripts in a script column
 had a function that calculated the side-position-elements grob-array using
 the sorting algorithm at the heart of script-column.cc.

 Another way to think of this is that, in general, we'd try to eliminate
 grobs like NoteColumn, ScriptColumn, DynamicLineSpanner, etc that only do
 traffic-coppery.  Or, inversely, we'd say that positioning only happens via
 traffic-cop grobs and no grob has the right to position itself.  But it
 seems like we need to pick one.


I gave this some thought a while ago, and concluded that it isn't possible
to get rid of the traffic cops (however, I'd be happy to be proven wrong).
My eventual decision was to have them everywhere. A grob could suggest its
own position using a callback, and if no other grob wanted to override
that, then the System grob would take the traffic cop role and set the
grob's position to whatever its suggestion was. That way you could write a
lot of the positioning code as callbacks, and the cops would only kick in
where necessary. There were also explicit dependencies so you could figure
out which grob was in charge of positioning for which other grob.

Cheers,
Joe
___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread David Kastrup
Han-Wen Nienhuys hanw...@gmail.com writes:

 In order to do cache invalidation, you will have to construct the
 reverse graph. If A.x depends on B.y, now A points to B. For proper
 cache invalidation, if B.y changes, then A.x becomes invalid. This
 means that A has to store a back-reference to B. Hence all pointers
 have to be stored both ways (including inverting 1-N relations into
 N-1 relations), and the both-way links have to be rewritten correctly
 during line breaking.

You can assign a generational count to properties.  If the generational
count of any dependency is higher than that of the dependent property,
it needs to get regenerated.  As long as the dependencies don't get lost
(and we use arbitrary size integers, resetting for each session), this
should be pretty straightforward and not require backwards links.  It
just requires double-checking your dependencies for change.

-- 
David Kastrup


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel


Re: Project - Eliminating grob parents and outside-staff-priority

2012-09-26 Thread m...@mikesolomon.org
On 26 sept. 2012, at 21:51, Han-Wen Nienhuys hanw...@gmail.com wrote:

 On Wed, Sep 26, 2012 at 9:17 PM, m...@mikesolomon.org
 m...@mikesolomon.org wrote:
 
 Another way to think of this is that, in general, we'd try to eliminate
 grobs like NoteColumn, ScriptColumn, DynamicLineSpanner, etc that only do
 traffic-coppery.  Or, inversely, we'd say that positioning only happens via
 traffic-cop grobs and no grob has the right to position itself.  But it
 seems like we need to pick one.
 
 The reason for trafic-coppery is that for many positioning schemes,
 there is no inherent order that can determine how two objects of the
 same type (and hence, with the same set of callbacks) should be
 typeset.
 
 A typical example of this is NoteCollision of N NoteColumns. The
 NoteColumns have to all move in a coordinated way, and the easiest way
 is to have a function (with local variables) that determines what has
 to happen. You might get around it, by having a bunch of properties
 instead, but you'd still have to store those somewhere, ie in the
 NoteCollision grob.

Not necessarily, it'd just make computation time longer.  If each note column 
had other concurrent note columns in its side-position-elements grob-array and 
we kept the same function from note-column.cc but rewrote it such that instead 
of translating axes by values it returned the offset value for a given note 
column, the function would be called N times for N note columns.  It's a 
tradeoff between efficiency and consistency - I'm not sure which one is better, 
but I'm sure it's possible to eliminate the NoteCollision grob at the cost of 
redundancy.

 
 Agreed, but this is just one instance of translate_axis, which is just one
 example of a side-effect in lilypond. Is your eventual goal to remove them
 all?
 
 
 Yes - that'd be great.  That'll make explicit dependencies a lot easier to
 handle - everything can be calculated in terms of callbacks.
 
 I'm all for removing side effects, but you can pursue that separately
 without trying to rewrite the backend.

My goal is not to rewrite the backend.  My goal is to come up with a 
generalized way to do multiple passes at many places in the compilation 
process, and the best way seems to be deleting certain cached properties.  I'm 
all for other ideas, though.

 
 
 To keep the main thing the main thing, the goal is to be able to wipe
 certain caches, restore the original callbacks, and know that all grobs
 properties that depended on the restored property will also be recalculated.
 My idea is an idea for simplifying LilyPond so that this work is easier, but
 there is no point in doing that if it won't help reach that goal.  I'm open
 to any and all suggestions.
 
 Have you thought this through?
 
 In order to do cache invalidation, you will have to construct the
 reverse graph. If A.x depends on B.y, now A points to B. For proper
 cache invalidation, if B.y changes, then A.x becomes invalid. This
 means that A has to store a back-reference to B. Hence all pointers
 have to be stored both ways (including inverting 1-N relations into
 N-1 relations), and the both-way links have to be rewritten correctly
 during line breaking.

I'm sure this is doable - it'd just be a royal pain.  It'd meant that 
get_property would have to take the me grob as an argument and somehow make 
the grobs aware of each other.

Cheers,
MS


___
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel