]> git.donarmstrong.com Git - lilypond.git/blob - lily/tuplet-bracket.cc
(after_line_breaking): suicide empty
[lilypond.git] / lily / tuplet-bracket.cc
1 /*
2   tuplet-bracket.cc -- implement Tuplet_bracket
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Jan Nieuwenhuizen <janneke@gnu.org>
7   Han-Wen Nienhuys <hanwen@xs4all.nl>
8 */
9
10 /*
11   TODO:
12
13   - tuplet bracket should probably be subject to the same rules as
14   beam sloping/quanting.
15
16   - There is no support for kneed brackets, or nested brackets.
17
18   - number placement for parallel beams should be much more advanced:
19   for sloped beams some extra horizontal offset must be introduced.
20
21   - number placement is usually done over the center note, not the
22   graphical center.
23 */
24
25 /*
26   TODO: quantise, we don't want to collide with staff lines.
27   (or should we be above staff?)
28
29   todo: handle breaking elegantly.
30 */
31
32
33 #include "tuplet-bracket.hh"
34 #include "line-interface.hh"
35 #include "beam.hh"
36 #include "warn.hh"
37 #include "font-interface.hh"
38 #include "output-def.hh"
39 #include "text-interface.hh"
40 #include "stem.hh"
41 #include "note-column.hh"
42 #include "pointer-group-interface.hh"
43 #include "directional-element-interface.hh"
44 #include "spanner.hh"
45 #include "staff-symbol-referencer.hh"
46 #include "lookup.hh"
47
48 static Item *
49 get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
50 {
51   Spanner *me = dynamic_cast<Spanner *> (me_grob);
52   Item *g = me->get_bound (hdir);
53   if (Note_column::has_interface (g)
54       && Note_column::get_stem (g)
55       && Note_column::dir (g) == my_dir)
56     g = Note_column::get_stem (g);
57
58   return g;
59 }
60
61 Grob *
62 Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool *equally_long)
63 {
64   Spanner *me = dynamic_cast<Spanner *> (me_grob);
65
66   if (me->get_bound (LEFT)->break_status_dir ()
67       || me->get_bound (RIGHT)->break_status_dir ())
68     return 0;
69
70   Grob *s1 = Note_column::get_stem (cols[0]);
71   Grob *s2 = Note_column::get_stem (cols.top ());
72
73   if (s2 != me->get_bound (RIGHT))
74     return 0;
75
76   Grob *b1 = s1 ? Stem::get_beam (s1) : 0;
77   Grob *b2 = s2 ? Stem::get_beam (s2) : 0;
78
79   *equally_long = false;
80   if (! (b1 && (b1 == b2) && !me->is_broken ()))
81     return 0;
82
83   extract_grob_set (b1, "stems", beam_stems);
84   if (beam_stems.size () == 0)
85     {
86       programming_error ("beam under tuplet bracket has no stems");
87       *equally_long = 0;
88       return 0;
89     }
90
91   *equally_long = (beam_stems[0] == s1 && beam_stems.top () == s2);
92   return b1;
93 }
94
95 /*
96   TODO:
97
98   in the case that there is no bracket, but there is a (single) beam,
99   follow beam precisely for determining tuplet number location.
100 */
101 MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, 1);
102 SCM
103 Tuplet_bracket::print (SCM smob)
104 {
105   Spanner *me = unsmob_spanner (smob);
106   Stencil mol;
107   extract_grob_set (me, "note-columns", columns);
108
109   {
110     SCM lp = me->get_property ("left-position");
111     SCM rp = me->get_property ("right-position");
112
113     if (!scm_is_number (rp) || !scm_is_number (lp))
114       {
115         /*
116           UGH. dependency tracking!
117         */
118         extract_grob_set (me, "tuplets", tuplets);
119         for (int i = 0; i < tuplets.size (); i++)
120           Tuplet_bracket::print (tuplets[i]->self_scm ());
121
122         after_line_breaking (smob);
123       }
124   }
125
126   Real ly = robust_scm2double (me->get_property ("left-position"), 0);
127   Real ry = robust_scm2double (me->get_property ("right-position"), 0);
128
129   bool equally_long = false;
130   Grob *par_beam = parallel_beam (me, columns, &equally_long);
131
132   Spanner *sp = dynamic_cast<Spanner *> (me);
133
134   bool bracket_visibility = !(par_beam && equally_long);
135   bool number_visibility = true;
136
137   /*
138     Fixme: the type of this prop is sucky.
139   */
140   SCM bracket = me->get_property ("bracket-visibility");
141   if (scm_is_bool (bracket))
142     bracket_visibility = ly_scm2bool (bracket);
143   else if (bracket == ly_symbol2scm ("if-no-beam"))
144     bracket_visibility = !par_beam;
145
146   SCM numb = me->get_property ("number-visibility");
147   if (scm_is_bool (numb))
148     number_visibility = ly_scm2bool (numb);
149   else if (numb == ly_symbol2scm ("if-no-beam"))
150     number_visibility = !par_beam;
151
152   Grob *commonx = common_refpoint_of_array (columns, me, X_AXIS);
153   commonx = commonx->common_refpoint (sp->get_bound (LEFT), X_AXIS);
154   commonx = commonx->common_refpoint (sp->get_bound (RIGHT), X_AXIS);
155
156   Direction dir = get_grob_direction (me);
157
158   Drul_array<Item *> bounds;
159   bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
160   bounds[RIGHT] = get_x_bound_item (me, RIGHT, dir);
161
162   Drul_array<bool> connect_to_other;
163   Interval x_span;
164   Direction d = LEFT;
165   do
166     {
167       x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
168       Direction break_dir = bounds[d]->break_status_dir ();
169       Spanner *orig_spanner = dynamic_cast<Spanner *> (me->original_);
170
171       int neighbor_idx = me->get_break_index () - break_dir;
172
173       /*
174         UGH. dependency handling.
175        */
176       if (break_dir
177           && d == RIGHT
178           && neighbor_idx < orig_spanner->broken_intos_.size ())
179         {
180           Grob *neighbor = orig_spanner->broken_intos_[neighbor_idx];
181
182           // ugh, should inspect callback?  
183           Tuplet_bracket::after_line_breaking (neighbor->self_scm ());
184         }
185
186       connect_to_other[d]
187         = (break_dir
188            && (neighbor_idx < orig_spanner->broken_intos_.size ()
189                && neighbor_idx >= 0)
190            && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
191            
192
193       if (connect_to_other[d])
194         {
195           Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
196                                                Interval (-0.5, 0.0)));
197
198           if (d == RIGHT)
199             x_span[d] += d * overshoot[d];
200           else
201             x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
202               - overshoot[LEFT];
203         }
204       else if (d == RIGHT
205                && (columns.is_empty ()
206                    || (bounds[d]->get_column ()
207                        != dynamic_cast<Item *> (columns.top ())->get_column ())))
208         {
209           /*
210             TODO: make padding tunable?
211           */
212           x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT] - 1.0;
213         }
214     }
215   while (flip (&d) != LEFT);
216
217   Real w = x_span.length ();
218   SCM number = me->get_property ("text");
219
220   Output_def *pap = me->get_layout ();
221   Stencil num;
222   if (scm_is_string (number) && number_visibility)
223     {
224       SCM properties = Font_interface::text_font_alist_chain (me);
225       SCM snum = Text_interface::interpret_markup (pap->self_scm (),
226                                                    properties, number);
227       num = *unsmob_stencil (snum);
228       num.align_to (X_AXIS, CENTER);
229       num.translate_axis (w / 2, X_AXIS);
230       num.align_to (Y_AXIS, CENTER);
231
232       num.translate_axis ((ry - ly) / 2, Y_AXIS);
233
234       mol.add_stencil (num);
235     }
236
237   /*
238     No bracket when it would be smaller than the number.
239
240     TODO: should use GAP in calculation too.
241   */
242   if (bracket_visibility && number_visibility
243       && mol.extent (X_AXIS).length () > w)
244     bracket_visibility = false;
245
246   if (bracket_visibility)
247     {
248       Real gap = 0.;
249
250       if (!num.extent (X_AXIS).is_empty ())
251         gap = num.extent (X_AXIS).length () + 1.0;
252
253       Drul_array<Real> zero (0, 0);
254       Real ss = Staff_symbol_referencer::staff_space (me);
255       Drul_array<Real> height
256         = robust_scm2drul (me->get_property ("edge-height"), zero);
257       Drul_array<Real> flare
258         = robust_scm2drul (me->get_property ("bracket-flare"), zero);
259       Drul_array<Real> shorten
260         = robust_scm2drul (me->get_property ("shorten-pair"), zero);
261       Drul_array<Stencil> edge_stencils;
262
263       scale_drul (&height, -ss * dir);
264       scale_drul (&flare, ss);
265       scale_drul (&shorten, ss);
266       do
267         {
268           if (connect_to_other[d])
269             {
270               height[d] = 0.0;
271               flare[d] = 0.0;
272               shorten[d] = 0.0;
273
274               SCM edge_text = me->get_property ("edge-text");
275
276               if (scm_is_pair (edge_text))
277                 {
278                   SCM properties = Font_interface::text_font_alist_chain (me);
279                   SCM text = index_get_cell (edge_text, d);
280                   if (Text_interface::is_markup (text))
281                     {
282                       SCM t = Text_interface::interpret_markup (pap->self_scm (), properties, text);
283
284                       Stencil *edge_text = unsmob_stencil (t);
285                       edge_text->translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
286                       edge_stencils[d] = *edge_text;
287                     }
288                 }
289             }
290         }
291       while (flip (&d) != LEFT);
292
293       Stencil brack = make_bracket (me, Y_AXIS,
294                                     Offset (w, ry - ly),
295                                     height,
296                                     /*
297                                       0.1 = more space at right due to italics
298                                       TODO: use italic correction of font.
299                                     */
300                                     Interval (-0.5, 0.5) * gap + 0.1,
301                                     flare, shorten);
302
303       do
304         {
305           if (!edge_stencils[d].is_empty ())
306             brack.add_stencil (edge_stencils[d]);
307         }
308       while (flip (&d) != LEFT);
309
310       mol.add_stencil (brack);
311     }
312
313   mol.translate_axis (ly, Y_AXIS);
314   mol.translate_axis (x_span[LEFT]
315                       - sp->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS), X_AXIS);
316   return mol.smobbed_copy ();
317 }
318
319 /*
320   should move to lookup?
321
322   TODO: this will fail for very short (shorter than the flare)
323   brackets.
324 */
325 Stencil
326 Tuplet_bracket::make_bracket (Grob *me, // for line properties.
327                               Axis protusion_axis,
328                               Offset dz,
329                               Drul_array<Real> height,
330                               Interval gap,
331                               Drul_array<Real> flare,
332                               Drul_array<Real> shorten)
333 {
334   Drul_array<Offset> corners (Offset (0, 0), dz);
335
336   Real length = dz.length ();
337   Drul_array<Offset> gap_corners;
338
339   Axis bracket_axis = other_axis (protusion_axis);
340
341   Drul_array<Offset> straight_corners = corners;
342
343   Direction d = LEFT;
344   do
345     straight_corners[d] += -d * shorten[d] / length * dz;
346   while (flip (&d) != LEFT)
347     ;
348
349   if (gap.is_empty ())
350     gap = Interval (0, 0);
351   do
352     gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
353   while (flip (&d) != LEFT)
354     ;
355
356   Drul_array<Offset> flare_corners = straight_corners;
357   do
358     {
359       flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
360       flare_corners[d][protusion_axis] += height[d];
361       straight_corners[d][bracket_axis] += -d * flare[d];
362     }
363   while (flip (&d) != LEFT);
364
365   Stencil m;
366   do
367     {
368       m.add_stencil (Line_interface::line (me, straight_corners[d],
369                                            gap_corners[d]));
370
371       m.add_stencil (Line_interface::line (me, straight_corners[d],
372                                            flare_corners[d]));
373     }
374   while (flip (&d) != LEFT);
375
376   return m;
377 }
378
379 void
380 Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
381 {
382   extract_grob_set (me, "note-columns", columns);
383   int l = 0;
384   while (l < columns.size () && Note_column::has_rests (columns[l]))
385     l++;
386
387   int r = columns.size ()- 1;
388   while (r >= l && Note_column::has_rests (columns[r]))
389     r--;
390
391   *left = *right = 0;
392
393   if (l <= r)
394     {
395       *left = columns[l];
396       *right = columns[r];
397     }
398 }
399
400 /*
401   use first -> last note for slope, and then correct for disturbing
402   notes in between.  */
403 void
404 Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
405 {
406   Spanner *me = dynamic_cast<Spanner *> (me_grob);
407
408   extract_grob_set (me, "note-columns", columns);
409   extract_grob_set (me, "tuplets", tuplets);
410
411   Grob *commony = common_refpoint_of_array (columns, me, Y_AXIS);
412   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
413   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
414     commony = st->common_refpoint (commony, Y_AXIS);
415
416   Grob *commonx = common_refpoint_of_array (columns, me, X_AXIS);
417   commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
418   commonx = commonx->common_refpoint (me->get_bound (LEFT), X_AXIS);
419   commonx = commonx->common_refpoint (me->get_bound (RIGHT), X_AXIS);
420
421   Interval staff;
422   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
423     {
424       staff = st->extent (commony, Y_AXIS);
425       Real pad = robust_scm2double (me->get_property ("staff-padding"), 0.5);
426       staff.widen (pad);
427     }
428   
429   Direction dir = get_grob_direction (me);
430
431   /*
432     Use outer non-rest columns to determine slope
433   */
434   Grob *left_col = 0;
435   Grob *right_col = 0;
436   get_bounds (me, &left_col, &right_col);
437   if (left_col && right_col)
438     {
439       Interval rv = right_col->extent (commony, Y_AXIS);
440       Interval lv = left_col->extent (commony, Y_AXIS);
441       rv.unite (staff);
442       lv.unite (staff);
443       Real graphical_dy = rv[dir] - lv[dir];
444
445       Slice ls = Note_column::head_positions_interval (left_col);
446       Slice rs = Note_column::head_positions_interval (right_col);
447
448       Interval musical_dy;
449       musical_dy[UP] = rs[UP] - ls[UP];
450       musical_dy[DOWN] = rs[DOWN] - ls[DOWN];
451       if (sign (musical_dy[UP]) != sign (musical_dy[DOWN]))
452         *dy = 0.0;
453       else if (sign (graphical_dy) != sign (musical_dy[DOWN]))
454         *dy = 0.0;
455       else
456         *dy = graphical_dy;
457     }
458   else
459     *dy = 0;
460
461   *offset = -dir * infinity_f;
462
463   Item *lgr = get_x_bound_item (me, LEFT, dir);
464   Item *rgr = get_x_bound_item (me, RIGHT, dir);
465   Real x0 = robust_relative_extent (lgr, commonx, X_AXIS)[LEFT];
466   Real x1 = robust_relative_extent (rgr, commonx, X_AXIS)[RIGHT];
467
468   Array<Offset> points;
469   points.push (Offset (x0 - x0, staff[dir]));
470   points.push (Offset (x1 - x0, staff[dir]));
471
472   for (int i = 0; i < columns.size (); i++)
473     {
474       Interval note_ext = columns[i]->extent (commony, Y_AXIS);
475       Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
476
477       Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
478       points.push (Offset (x, notey));
479     }
480
481   /*
482     This is a slight hack. We compute two encompass points from the
483     bbox of the smaller tuplets.
484
485     We assume that the smaller bracket is 1.0 space high.
486   */
487   Real ss = Staff_symbol_referencer::staff_space (me);
488   for (int i = 0; i < tuplets.size (); i++)
489     {
490       Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
491       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
492
493       Direction d = LEFT;
494       Real lp = scm_to_double (tuplets[i]->get_property ("left-position"));
495       Real rp = scm_to_double (tuplets[i]->get_property ("right-position"));
496       Real other_dy = rp - lp;
497
498       do
499         {
500           Real y
501             = tuplet_y.linear_combination (d * sign (other_dy));
502
503 #if 0
504           /*
505             Let's not take padding into account for nested tuplets.
506             the edges can come very close to the stems, likewise for
507             nested tuplets?
508           */
509           Drul_array<Real> my_height
510             = robust_scm2drul (me->get_property ("edge-height"),
511                                Interval (0, 0));
512           if (dynamic_cast<Spanner *> (tuplets[i])->get_bound (d)
513               == me->get_bound (d))
514             y += dir * my_height[d];
515 #endif
516
517           points.push (Offset (tuplet_x[d] - x0, y));
518         }
519       while (flip (&d) != LEFT);
520     }
521
522   Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
523   for (int i = 0; i < points.size (); i++)
524     {
525       Real x = points[i][X_AXIS];
526       Real tuplety = (*dy) * x * factor;
527
528       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
529         *offset = points[i][Y_AXIS] - tuplety;
530     }
531
532   *offset += scm_to_double (me->get_property ("padding")) * dir;
533
534   /*
535     horizontal brackets should not collide with staff lines.
536
537     Kind of pointless since we put them outside the staff anyway, but
538     let's leave code for the future when possibly allow them to move
539     into the staff once again.
540   */
541   if (*dy == 0
542       && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
543     {
544       // quantize, then do collision check.
545       *offset *= 2 / ss;
546
547       *offset = rint (*offset);
548       if (Staff_symbol_referencer::on_staffline (me, (int) rint (*offset)))
549         *offset += dir;
550
551       *offset *= 0.5 * ss;
552     }
553 }
554
555 /*
556   We depend on the beams if there are any.
557 */
558 MAKE_SCHEME_CALLBACK (Tuplet_bracket, before_line_breaking, 1);
559 SCM
560 Tuplet_bracket::before_line_breaking (SCM smob)
561 {
562   Grob *me = unsmob_grob (smob);
563   extract_grob_set (me, "note-columns", columns);
564
565   for (int i = columns.size (); i--;)
566     {
567       Grob *s = Note_column::get_stem (columns[i]);
568       Grob *b = s ? Stem::get_beam (s) : 0;
569       if (b)
570         me->add_dependency (b);
571     }
572   return SCM_UNSPECIFIED;
573 }
574
575 MAKE_SCHEME_CALLBACK (Tuplet_bracket, after_line_breaking, 1);
576 SCM
577 Tuplet_bracket::after_line_breaking (SCM smob)
578 {
579   Grob *me = unsmob_grob (smob);
580   extract_grob_set (me, "note-columns", columns);
581
582   if (columns.is_empty())
583     {
584       me->suicide ();
585       return SCM_UNSPECIFIED;
586     }
587   
588   Direction dir = get_grob_direction (me);
589   if (!dir)
590     {
591       dir = Tuplet_bracket::get_default_dir (me);
592       set_grob_direction (me, dir);
593     }
594
595   bool equally_long = false;
596   Grob *par_beam = parallel_beam (me, columns, &equally_long);
597
598   /*
599     We follow the beam only if there is one, and we are next to it.
600   */
601   Real dy = 0.0;
602   Real offset = 0.0;
603   if (!par_beam
604       || get_grob_direction (par_beam) != dir)
605     calc_position_and_height (me, &offset, &dy);
606   else
607     {
608       SCM ps = par_beam->get_property ("positions");
609
610       Real lp = scm_to_double (scm_car (ps));
611       Real rp = scm_to_double (scm_cdr (ps));
612
613       /*
614         duh. magic.
615       */
616       offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
617       dy = rp - lp;
618     }
619
620   SCM lp = me->get_property ("left-position");
621   SCM rp = me->get_property ("right-position");
622
623   if (scm_is_number (lp) && !scm_is_number (rp))
624     rp = scm_from_double (scm_to_double (lp) + dy);
625   else if (scm_is_number (rp) && !scm_is_number (lp))
626     lp = scm_from_double (scm_to_double (rp) - dy);
627   else if (!scm_is_number (rp) && !scm_is_number (lp))
628     {
629       lp = scm_from_double (offset);
630       rp = scm_from_double (offset + dy);
631     }
632
633   me->set_property ("left-position", lp);
634   me->set_property ("right-position", rp);
635
636   return SCM_UNSPECIFIED;
637 }
638
639 /*
640   similar to beam ?
641 */
642 Direction
643 Tuplet_bracket::get_default_dir (Grob *me)
644 {
645   Drul_array<int> dirs (0, 0);
646   extract_grob_set (me, "note-columns", columns);
647   for (int i = 0; i < columns.size (); i++)
648     {
649       Grob *nc = columns[i];
650       Direction d = Note_column::dir (nc);
651       if (d)
652         dirs[d]++;
653     }
654
655   return dirs[UP] >= dirs[DOWN] ? UP : DOWN;
656 }
657
658 void
659 Tuplet_bracket::add_column (Grob *me, Item *n)
660 {
661   Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
662   me->add_dependency (n);
663
664   add_bound_item (dynamic_cast<Spanner *> (me), n);
665 }
666
667 void
668 Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
669 {
670   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
671   me->add_dependency (bracket);
672 }
673
674 ADD_INTERFACE (Tuplet_bracket,
675                "tuplet-bracket-interface",
676                "A bracket with a number in the middle, used for tuplets. "
677                "When the bracket spans  a line break, the value of "
678                "@code{break-overshoot} determines how far it extends "
679                "beyond the staff. "
680                "At a line break, the markups in the @code{edge-text} are printed "
681                "at the edges. ",
682
683                
684                "bracket-flare "
685                "bracket-visibility "
686                "break-overshoot "
687                "direction "
688                "edge-height "
689                "edge-text "
690                "left-position "
691                "note-columns "
692                "number-visibility "
693                "padding "
694                "right-position "
695                "shorten-pair "
696                "staff-padding "
697                "thickness "
698                "tuplets ");
699