]> git.donarmstrong.com Git - lilypond.git/commitdiff
ledger-line-spanner.cc: refactor Ledger_line_spanner::print
authorPaul Morris <paulwmorris@gmail.com>
Thu, 14 Apr 2016 02:10:26 +0000 (22:10 -0400)
committerPaul Morris <paulwmorris@gmail.com>
Tue, 19 Apr 2016 14:14:12 +0000 (10:14 -0400)
Optimizations and removal of code duplication.

Only iterate through all note heads once, generating
all relevant data for just note heads involved in
ledger lines.  Then work with that data.

Merge overlapping ledger data before producing
ledger stencils so we only produce one stencil for
each ledger line.

lily/ledger-line-spanner.cc

index b0e8fee3c2321b8e7f078a7946048714deca168a..42d32b496af7bb5ec7a15f1b52cdbc4ea84959c8 100644 (file)
@@ -140,16 +140,33 @@ Ledger_line_spanner::set_spacing_rods (SCM smob)
   return SCM_UNSPECIFIED;
 }
 
-struct Ledger_request
+struct Head_data
 {
-  Interval ledger_extent_;
-  Interval head_extent_;
   int position_;
-  Ledger_request ()
+  vector<Real> ledger_positions_;
+  Interval head_extent_;
+  Interval ledger_extent_;
+  Interval accidental_extent_;
+  Head_data ()
   {
-    ledger_extent_.set_empty ();
     head_extent_.set_empty ();
-    position_ = 0;
+    ledger_extent_.set_empty ();
+    accidental_extent_.set_empty ();
+  }
+};
+
+struct Ledger_request
+{
+  Interval max_ledger_extent_;
+  Interval max_head_extent_;
+  int max_position_;
+  vector <Head_data> heads_;
+  map <Real, Interval> ledger_extents_;
+  Ledger_request ()
+  {
+    max_ledger_extent_.set_empty ();
+    max_head_extent_.set_empty ();
+    max_position_ = 0;
   }
 };
 
@@ -165,12 +182,12 @@ Ledger_line_spanner::print (SCM smob)
 {
   Spanner *me = unsmob<Spanner> (smob);
 
+  // Generate ledger requests from note head properties, etc.
   extract_grob_set (me, "note-heads", heads);
 
   if (heads.empty ())
     return SCM_EOL;
 
-  // find size of note heads.
   Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
   if (!staff)
     return SCM_EOL;
@@ -183,17 +200,11 @@ Ledger_line_spanner::print (SCM smob)
   Real length_fraction
     = robust_scm2double (me->get_property ("length-fraction"), 0.25);
 
-  Stencil ledgers;
-
-  Grob *common[NO_AXES];
-
-  for (int i = X_AXIS; i < NO_AXES; i++)
+  Grob *common_x = common_refpoint_of_array (heads, me, X_AXIS);
+  for (vsize i = heads.size (); i--;)
     {
-      Axis a = Axis (i);
-      common[a] = common_refpoint_of_array (heads, me, a);
-      for (vsize i = heads.size (); i--;)
-        if (Grob *g = unsmob<Grob> (me->get_object ("accidental-grob")))
-          common[a] = common[a]->common_refpoint (g, a);
+      if (Grob *g = unsmob<Grob> (heads[i]->get_object ("accidental-grob")))
+        common_x = common_x->common_refpoint (g, X_AXIS);
     }
 
   Ledger_requests reqs;
@@ -202,23 +213,43 @@ Ledger_line_spanner::print (SCM smob)
       Item *h = dynamic_cast<Item *> (heads[i]);
 
       int pos = Staff_symbol_referencer::get_rounded_position (h);
-      if (pos && !staff_extent.contains (pos))
+      vector<Real> ledger_positions =
+        Staff_symbol::ledger_positions (staff, pos);
+
+      // We work with all notes that produce ledgers and any notes that
+      // fall outside the staff that do not produce ledgers, such as
+      // notes in the first space just beyond the staff.
+      if (ledger_positions.size () != 0 || !staff_extent.contains (pos))
         {
-          Interval head_extent = h->extent (common[X_AXIS], X_AXIS);
+          Interval head_extent = h->extent (common_x, X_AXIS);
           Interval ledger_extent = head_extent;
           ledger_extent.widen (length_fraction * head_extent.length ());
 
-          Direction vdir = Direction (sign (pos));
+          Direction vdir = Direction (sign (pos != 0 ? pos : 1));
           int rank = h->get_column ()->get_rank ();
 
-          reqs[rank][vdir].ledger_extent_.unite (ledger_extent);
-          reqs[rank][vdir].head_extent_.unite (head_extent);
-          reqs[rank][vdir].position_
-            = vdir * max (vdir * reqs[rank][vdir].position_, vdir * pos);
+          reqs[rank][vdir].max_ledger_extent_.unite (ledger_extent);
+          reqs[rank][vdir].max_head_extent_.unite (head_extent);
+          reqs[rank][vdir].max_position_
+            = vdir * max (vdir * reqs[rank][vdir].max_position_,
+                          vdir * pos);
+          Head_data hd;
+          hd.position_ = pos;
+          hd.ledger_positions_ = ledger_positions;
+          hd.ledger_extent_ = ledger_extent;
+          hd.head_extent_ = head_extent;
+          if (Grob *g = unsmob<Grob> (h->get_object ("accidental-grob")))
+            hd.accidental_extent_ = g->extent (common_x, X_AXIS);
+          reqs[rank][vdir].heads_.push_back(hd);
         }
     }
 
-  // determine maximum size for non-colliding ledger.
+  if (reqs.size () == 0)
+    return SCM_EOL;
+
+  // Iterate through ledger requests and when ledger lines will be
+  // too close together horizontally, shorten max_ledger_extent to
+  // produce more space between them.
   Real gap = robust_scm2double (me->get_property ("gap"), 0.1);
   Ledger_requests::iterator last (reqs.end ());
   for (Ledger_requests::iterator i (reqs.begin ());
@@ -229,81 +260,111 @@ Ledger_line_spanner::print (SCM smob)
 
       for (DOWN_and_UP (d))
         {
-          if (!staff_extent.contains (last->second[d].position_)
-              && !staff_extent.contains (i->second[d].position_))
+          // Some rank--> vdir--> reqs will be 'empty' because notes
+          // will not be above AND below the staff for a given rank.
+          if (!staff_extent.contains (last->second[d].max_position_)
+              && !staff_extent.contains (i->second[d].max_position_))
             {
+              // Midpoint between the furthest bounds of the two heads.
               Real center
-                = (last->second[d].head_extent_[RIGHT]
-                   + i->second[d].head_extent_[LEFT]) / 2;
+                = (last->second[d].max_head_extent_[RIGHT]
+                   + i->second[d].max_head_extent_[LEFT]) / 2;
+
+              // Do both reqs have notes further than the first space
+              // beyond the staff?
+              // (due tilt of quarter note-heads)
+              /* FIXME */
+              bool both
+                = (!staff_extent.contains (last->second[d].max_position_
+                                           - sign (last->second[d].max_position_))
+                   && !staff_extent.contains (i->second[d].max_position_
+                                              - sign (i->second[d].max_position_)));
 
               for (LEFT_and_RIGHT (which))
                 {
                   Ledger_request &lr = ((which == LEFT) ? * last : *i).second[d];
 
-                  // due tilt of quarter note-heads
-                  /* FIXME */
-                  bool both
-                    = (!staff_extent.contains (last->second[d].position_
-                                               - sign (last->second[d].position_))
-                       && !staff_extent.contains (i->second[d].position_
-                                                  - sign (i->second[d].position_)));
                   Real limit = (center + (both ? which * gap / 2 : 0));
-                  lr.ledger_extent_.at (-which)
-                    = which * max (which * lr.ledger_extent_[-which], which * limit);
+                  lr.max_ledger_extent_.at (-which)
+                    = which * max (which * lr.max_ledger_extent_[-which],
+                                   which * limit);
                 }
             }
         }
     }
 
-  // create ledgers for note heads
-  Real ledgerlinethickness
-    = Staff_symbol::get_ledger_line_thickness (staff);
-  for (vsize i = heads.size (); i--;)
+  // Iterate through ledger requests and the data they have about each
+  // note head to generate the final extents for all ledger lines.
+  // Note heads that are different widths produce different ledger
+  // extents and these are merged so the widest extent prevails
+  // (the union of the intervals) for each ledger line.
+  for (Ledger_requests::iterator i (reqs.begin ());
+       i != reqs.end (); i++)
     {
-      Item *h = dynamic_cast<Item *> (heads[i]);
-
-      int pos = Staff_symbol_referencer::get_rounded_position (h);
-      vector<Real> ledger_positions = Staff_symbol::ledger_positions (staff, pos);
-      if (!ledger_positions.empty ())
+      for (DOWN_and_UP (d))
         {
-          int ledger_count = ledger_positions.size ();
-          Interval head_size = h->extent (common[X_AXIS], X_AXIS);
-          Interval ledger_size = head_size;
-          ledger_size.widen (ledger_size.length () * length_fraction);
-
-          if (pos && !staff_extent.contains (pos))
+          Ledger_request &lr = i->second[d];
+          for (vsize h = 0; h < lr.heads_.size (); h++)
             {
-              Interval max_size = reqs[h->get_column ()->get_rank ()]
-                                  [Direction (sign (pos))].ledger_extent_;
-
-              if (!max_size.is_empty ())
-                ledger_size.intersect (max_size);
+              vector<Real> &ledger_posns = lr.heads_[h].ledger_positions_;
+              Interval &ledger_size = lr.heads_[h].ledger_extent_;
+              Interval &head_size = lr.heads_[h].head_extent_;
+              Interval &acc_extent = lr.heads_[h].accidental_extent_;
+
+              // Limit ledger extents to a maximum to preserve space
+              // between ledgers when note heads get close.
+              if (!lr.max_ledger_extent_.is_empty ())
+                ledger_size.intersect (lr.max_ledger_extent_);
+
+              // Iterate through the ledgers for a given note head.
+              for (vsize l = 0; l < ledger_posns.size (); l++)
+                {
+                  Real lpos = ledger_posns[l];
+                  Interval x_extent = ledger_size;
+
+                  // Notes with accidental signs get shorter ledgers.
+                  // (Only happens for the furthest note in the column.)
+                  if (l == 0 && !acc_extent.is_empty ())
+                    {
+                      Real dist
+                        = linear_combination (Drul_array<Real> (acc_extent[RIGHT],
+                                                                head_size[LEFT]),
+                                              0.0);
+
+                      Real left_shorten = max (-ledger_size[LEFT] + dist, 0.0);
+                      x_extent[LEFT] += left_shorten;
+                      /*
+                        TODO: shorten 2 ledger lines for the case
+                        natural + downstem.
+                      */
+                    }
+                  if (lr.ledger_extents_.find (lpos) == lr.ledger_extents_.end ())
+                    lr.ledger_extents_[lpos] = x_extent;
+                  else
+                    lr.ledger_extents_[lpos].unite (x_extent);
+                }
             }
+        }
+    }
 
-          for (int i = 0; i < ledger_count; i++)
-            {
-              Real lpos = ledger_positions[i];
-              Interval x_extent = ledger_size;
-
-              if (i == 0)
-                if (Grob *g = unsmob<Grob> (h->get_object ("accidental-grob")))
-                  {
-                    Interval accidental_size = g->extent (common[X_AXIS], X_AXIS);
-                    Real d
-                      = linear_combination (Drul_array<Real> (accidental_size[RIGHT],
-                                                              head_size[LEFT]),
-                                            0.0);
-
-                    Real left_shorten = max (-ledger_size[LEFT] + d, 0.0);
-
-                    x_extent[LEFT] += left_shorten;
-                    /*
-                      TODO: shorten 2 ledger lines for the case natural +
-                      downstem.
-                    */
-                  }
+  // Create the stencil for the ledger line spanner by iterating
+  // through the ledger requests and their data on ledger extents.
+  Stencil ledgers;
+  Real ledgerlinethickness
+    = Staff_symbol::get_ledger_line_thickness (staff);
 
+  for (Ledger_requests::iterator i (reqs.begin ());
+       i != reqs.end (); i++)
+    {
+      for (DOWN_and_UP (d))
+        {
+          map<Real, Interval> &lex = i->second[d].ledger_extents_;
+          for (map<Real, Interval>::iterator k = lex.begin ();
+               k != lex.end (); k++)
+            {
               Real blotdiameter = ledgerlinethickness;
+              Real lpos = k->first;
+              Interval x_extent = k->second;
               Interval y_extent
                 = Interval (-0.5 * (ledgerlinethickness),
                             +0.5 * (ledgerlinethickness));
@@ -316,7 +377,7 @@ Ledger_line_spanner::print (SCM smob)
         }
     }
 
-  ledgers.translate_axis (-me->relative_coordinate (common[X_AXIS], X_AXIS),
+  ledgers.translate_axis (-me->relative_coordinate (common_x, X_AXIS),
                           X_AXIS);
 
   return ledgers.smobbed_copy ();