]> git.donarmstrong.com Git - lilypond.git/blob - lily/system.cc
3a0f2dbaa339d0744f575778c0d31121370c6c25
[lilypond.git] / lily / system.cc
1 /*
2   system.cc -- implement System
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "input-smob.hh"
10 #include "axis-group-interface.hh"
11 #include "warn.hh"
12 #include "system.hh"
13 #include "main.hh"
14 #include "paper-column.hh"
15 #include "paper-def.hh"
16 #include "paper-outputter.hh"
17 #include "paper-score.hh"
18 #include "string.hh"
19 #include "warn.hh"
20 #include "dimensions.hh"
21 #include "molecule.hh"
22 #include "all-font-metrics.hh"
23 #include "spacing-interface.hh"
24 #include "staff-symbol-referencer.hh"
25
26 // todo: use map.
27 void
28 fixup_refpoints (SCM s)
29 {
30   for (; gh_pair_p (s); s = ly_cdr (s))
31     {
32       Grob::fixup_refpoint (ly_car (s));
33     }
34 }
35
36
37 System::System (SCM s)
38   : Spanner (s)
39 {
40   rank_ = 0;
41 }
42
43 int
44 System::element_count () const
45 {
46   return scm_ilength (get_grob_property ("all-elements"));
47 }
48
49 int
50 System::spanner_count () const
51 {
52   int k =0;
53   for (SCM s = get_grob_property ("all-elements");
54        gh_pair_p (s); s = ly_cdr (s))
55     {
56       if (dynamic_cast<Spanner*> (unsmob_grob (gh_car(s))))
57         k++;
58     }
59
60   return k;
61 }
62   
63
64 int
65 scm_default_compare (const void * a, const void *b)
66 {
67   SCM pa = *(SCM *)a;
68   SCM pb = *(SCM *)b;
69
70   if (pa < pb) return -1 ;
71   else if (pa > pb) return 1;
72   else return 0;
73 }
74
75 /*
76   modify L in place: sort it 
77 */
78
79 SCM
80 uniquify_list (SCM l)
81 {
82   int len = scm_ilength (l);
83   SCM  * arr = new SCM[len];
84   int k = 0;
85   for (SCM s =l ; SCM_NNULLP (s); s = SCM_CDR(s))
86     arr[k++] = SCM_CAR(s);
87
88   assert (k == len);
89   qsort (arr, len, sizeof (SCM), &scm_default_compare);
90
91   k = 0;
92   SCM s =l;
93   for (int i = 0; i < len ; i++)
94     {
95       if (i && arr[i] == arr[i-1])
96         continue;
97
98       SCM_SETCAR(s, arr[i]);
99
100       if (i < len - 1)
101         s = SCM_CDR(s);
102     }
103
104   SCM_SETCDR(s, SCM_EOL);
105   delete arr;
106   
107   return l; 
108 }
109
110 void
111 System::typeset_grob (Grob * elem)
112 {
113   if (elem->pscore_)
114     programming_error ("Adding element twice.");
115   
116   elem->pscore_ = pscore_;
117   Pointer_group_interface::add_grob (this, ly_symbol2scm ("all-elements"), elem);
118   scm_gc_unprotect_object (elem->self_scm ());
119 }
120
121 void
122 System::output_lines ()
123 {
124   for (SCM s = get_grob_property ("all-elements");
125        gh_pair_p (s); s = ly_cdr (s))
126     {
127       Grob * g = unsmob_grob (ly_car (s));
128       if (g->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
129         {
130           /*
131             Kill no longer needed grobs. 
132            */
133           Item * it = dynamic_cast<Item*> (g);
134           if (it && Item::breakable_b(it))
135             {
136               it->find_prebroken_piece (LEFT)->suicide();
137               it->find_prebroken_piece (RIGHT)->suicide();
138             }
139           g->suicide ();
140         }
141       else if (g->live ())
142         g->do_break_processing ();
143     }
144
145   /*
146     fixups must be done in broken line_of_scores, because new elements
147     are put over there.  */
148   int count = 0;
149   for (int i=0; i < broken_intos_.size (); i++)
150     {
151       Grob *se = broken_intos_[i];
152       SCM all = se->get_grob_property ("all-elements");
153       for (SCM s = all; gh_pair_p (s); s = ly_cdr (s))
154         {
155           fixup_refpoint (ly_car (s));
156         }
157       count += scm_ilength (all);
158     }
159   
160   /*
161     needed for doing items.
162    */
163   fixup_refpoints (get_grob_property ("all-elements"));
164
165   
166   for (SCM s = get_grob_property ("all-elements");
167        gh_pair_p (s); s = ly_cdr (s))
168     {
169       unsmob_grob (ly_car (s))->handle_broken_dependencies ();
170     }
171   handle_broken_dependencies ();
172
173   /*
174     Because the this->get_grob_property (all-elements) contains items
175     in 3 versions, handle_broken_dependencies () will leave duplicated
176     items in all-elements. Strictly speaking this is harmless, but it
177     leads to duplicated symbols in the output. uniquify_list() makes
178     sure that no duplicates are in the list.
179    */
180   for (int i=0; i < broken_intos_.size (); i++)
181     {
182       /*
183         don't do this: strange side effects.
184        */
185       //    SCM al = broken_intos_[i]->get_grob_property ("all-elements");
186       //      al  = uniquify_list (al); 
187     }
188   
189   if (verbose_global_b)
190     progress_indication (_f ("Element count %d.",  count + element_count ()));
191
192   
193   for (int i=0; i < broken_intos_.size (); i++)
194     {
195       System *system = dynamic_cast<System*> (broken_intos_[i]);
196
197       if (verbose_global_b)
198         progress_indication ("[");
199       bool last = i+1 == broken_intos_.size ();
200       system->post_processing (last);
201
202       if (verbose_global_b)
203         {
204           progress_indication (to_string (i));
205           progress_indication ("]");
206         }
207
208       if (i < broken_intos_.size () - 1)
209         {
210           SCM lastcol =  ly_car (system->get_grob_property ("columns"));
211           Grob*  e = unsmob_grob (lastcol);
212
213           SCM between = ly_symbol2scm ("between-system-string");
214           SCM inter = e->internal_get_grob_property (between);
215           if (gh_string_p (inter))
216             {
217               pscore_->outputter_
218                 ->output_scheme (scm_list_n (between, 
219                                              inter, SCM_UNDEFINED));          
220             }
221         }
222     }
223 }
224
225
226
227
228 /*
229   Find the loose columns in POSNS, and drape them around the columns
230   specified in BETWEEN-COLS.  */
231 void
232 set_loose_columns (System* which, Column_x_positions const *posns)
233 {
234   for (int i = 0; i < posns->loose_cols_.size (); i++)
235     {
236       int divide_over = 1;
237       Item *loose = dynamic_cast<Item*> (posns->loose_cols_[i]);
238       Paper_column* col = dynamic_cast<Paper_column*> (loose);
239       
240       if (col->system_)
241         continue;
242       
243       Item * left = 0;
244       Item * right = 0;
245       do
246         {
247           SCM between = loose->get_grob_property ("between-cols");
248           if (!gh_pair_p (between))
249             break;
250
251
252           Item * l=dynamic_cast<Item*> (unsmob_grob (ly_car (between)));
253           Item * r=dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
254
255           if (!(l && r))
256             break ;
257           
258           if (!left && l)
259             {
260               left = l->get_column ();
261               if (!left->get_system ())
262                 left = left->find_prebroken_piece (RIGHT);
263             }
264
265           divide_over ++;
266           loose = right = r->get_column ();
267         }
268       while (1);
269
270       if (!right->get_system ())
271         right = right->find_prebroken_piece (LEFT);
272       
273       /*
274         We divide the remaining space of the column over the left and
275         right side. At the moment, we  
276         
277       */
278       Grob * common = right->common_refpoint (left, X_AXIS);
279       
280       Real rx = right->extent(common, X_AXIS)[LEFT];
281       Real lx = left->extent(common, X_AXIS)[RIGHT];
282       Real total_dx = rx - lx;
283       Interval cval =col->extent (col, X_AXIS);
284
285       /*
286         
287         We put it in the middle. This is not an ideal solution -- the
288         break alignment code inserts a fixed space before the clef
289         (about 1 SS), while the space following the clef is
290         flexible. In tight situations, the clef will almost be on top
291         of the following note. 
292         
293       */
294       Real dx = rx-lx - cval.length ();
295       if (total_dx < 2* cval.length ())
296         {
297           /*
298             todo: this is discontinuous. I'm too tired to
299             invent a sliding mechanism. Duh.
300
301             TODO.
302            */
303           dx *= 0.25;
304         }
305       else
306         dx *= 0.5;
307
308       col->system_ = which;
309       col->translate_axis (- col->relative_coordinate (common, X_AXIS), X_AXIS);
310       col->translate_axis (lx + dx - cval[LEFT], X_AXIS); 
311     }
312 }
313
314 // const?
315 void
316 System::break_into_pieces (Array<Column_x_positions> const &breaking)
317 {
318   for (int i=0; i < breaking.size (); i++)
319     {
320       System *system = dynamic_cast <System*> (clone ());
321       system->rank_ = i;
322
323       Link_array<Grob> c (breaking[i].cols_);
324       pscore_->typeset_line (system);
325       
326       system->set_bound (LEFT,c[0]);
327       system->set_bound (RIGHT,c.top ());
328       for (int j=0; j < c.size (); j++)
329         {
330           c[j]->translate_axis (breaking[i].config_[j],X_AXIS);
331           dynamic_cast<Paper_column*> (c[j])->system_ = system;
332         }
333       set_loose_columns (system, &breaking[i]);
334       broken_intos_.push (system);
335     }
336 }
337
338 void
339 System::output_molecule (SCM expr, Offset o)
340 {
341   while (1)
342     {
343       if (!gh_pair_p (expr))
344         return;
345   
346       SCM head =ly_car (expr);
347       if (unsmob_input (head))
348         {
349           Input * ip = unsmob_input (head);
350       
351           pscore_->outputter_->output_scheme (scm_list_n (ly_symbol2scm ("define-origin"),
352                                                            scm_makfrom0str (ip->file_string ().to_str0 ()),
353                                                            gh_int2scm (ip->line_number ()),
354                                                            gh_int2scm (ip->column_number ()),
355                                                            SCM_UNDEFINED));
356           expr = ly_cadr (expr);
357         }
358       else  if (head ==  ly_symbol2scm ("no-origin"))
359         {
360           pscore_->outputter_->output_scheme (scm_list_n (head, SCM_UNDEFINED));
361           expr = ly_cadr (expr);
362         }
363       else if (head == ly_symbol2scm ("translate-molecule"))
364         {
365           o += ly_scm2offset (ly_cadr (expr));
366           expr = ly_caddr (expr);
367         }
368       else if (head == ly_symbol2scm ("combine-molecule"))
369         {
370           output_molecule (ly_cadr (expr), o);
371           expr = ly_caddr (expr);
372         }
373       else
374         {
375           pscore_->outputter_->
376             output_scheme (scm_list_n (ly_symbol2scm ("placebox"),
377                                     gh_double2scm (o[X_AXIS]),
378                                     gh_double2scm (o[Y_AXIS]),
379                                     expr,
380                                     SCM_UNDEFINED));
381
382           return;
383         }
384     }
385 }
386
387 void
388 System::output_scheme (SCM s)
389 {
390   pscore_->outputter_->output_scheme (s);
391 }
392
393 void
394 System::add_column (Paper_column*p)
395 {
396   Grob *me = this;
397   SCM cs = me->get_grob_property ("columns");
398   Grob * prev =  gh_pair_p (cs) ? unsmob_grob (ly_car (cs)) : 0;
399
400   p->rank_ = prev ? Paper_column::get_rank (prev) + 1 : 0; 
401
402   me->set_grob_property ("columns",  gh_cons (p->self_scm (), cs));
403
404   Axis_group_interface::add_element (me, p);
405 }
406
407 void
408 System::pre_processing ()
409 {
410   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
411     unsmob_grob (ly_car (s))->discretionary_processing ();
412
413   if (verbose_global_b)
414     progress_indication (_f ("Grob count %d ",  element_count ()));
415
416   
417   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
418     unsmob_grob (ly_car (s))->handle_prebroken_dependencies ();
419   
420   fixup_refpoints (get_grob_property ("all-elements"));
421   
422   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
423     {
424       Grob* sc = unsmob_grob (ly_car (s));
425       sc->calculate_dependencies (PRECALCED, PRECALCING, ly_symbol2scm ("before-line-breaking-callback"));
426     }
427   
428   progress_indication ("\n" + _ ("Calculating line breaks...") + " ");
429   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
430     {
431       Grob * e = unsmob_grob (ly_car (s));
432       SCM proc = e->get_grob_property ("spacing-procedure");
433       if (gh_procedure_p (proc))
434         gh_call1 (proc, e->self_scm ());
435     }
436 }
437
438
439   const int LAYER_COUNT= 3;
440
441
442
443 void
444 System::post_processing (bool last_line)
445 {
446   for (SCM s = get_grob_property ("all-elements");
447        gh_pair_p (s); s = ly_cdr (s))
448     {
449       Grob* sc = unsmob_grob (ly_car (s));
450       sc->calculate_dependencies (POSTCALCED, POSTCALCING,
451                                   ly_symbol2scm ("after-line-breaking-callback"));
452     }
453
454   Interval i (extent (this, Y_AXIS));
455   if (i.is_empty ())
456     programming_error ("Huh?  Empty System?");
457   else
458     translate_axis (- i[MAX], Y_AXIS);
459
460   Real height = i.length ();
461   if (height > 50 CM)
462     {
463       programming_error ("Improbable system height");
464       height = 50 CM;
465     }
466
467   /*
468     generate all molecules  to trigger all font loads.
469
470     (ugh. This is not very memory efficient.)  */
471
472   SCM all = get_grob_property ("all-elements")  ;
473   all = uniquify_list (all);
474
475   /*
476     triger font loads first.
477
478     This might seem inefficient, but Molecules are cached per grob
479     anyway.
480     */
481   this->get_molecule();
482   for (SCM s = all; gh_pair_p (s); s = ly_cdr (s))
483     {
484       Grob * g = unsmob_grob (ly_car (s));
485       g->get_molecule ();
486     }
487   
488   /*
489     font defs;
490    */
491   SCM font_names = ly_quote_scm (get_paper ()->font_descriptions ());  
492   output_scheme (scm_list_n (ly_symbol2scm ("define-fonts"),
493                              font_names,
494                              SCM_UNDEFINED));
495
496   /*
497     line preamble.
498    */
499   Interval j (extent (this, X_AXIS));
500   Real length = j[RIGHT];
501     
502   output_scheme (scm_list_n (ly_symbol2scm ("start-system"),
503                           gh_double2scm (length),
504                           gh_double2scm (height),
505                           SCM_UNDEFINED));
506   
507   /* Output elements in three layers, 0, 1, 2.
508      The default layer is 1. */
509   {
510     Molecule *m = this->get_molecule();
511     if (m)
512       output_molecule (m->get_expr (), Offset(0,0));
513   }
514   
515   for (int i = 0; i < 3; i++)
516     for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s);
517          s = ly_cdr (s))
518       {
519         Grob *sc = unsmob_grob (ly_car (s));
520         Molecule *m = sc->get_molecule ();
521         if (!m)
522           continue;
523         
524         SCM s = sc->get_grob_property ("layer");
525         int layer = gh_number_p (s) ? gh_scm2int (s) : 1;
526         if (layer != i)
527           continue;
528         
529         Offset o (sc->relative_coordinate (this, X_AXIS),
530                   sc->relative_coordinate (this, Y_AXIS));
531         
532         SCM e = sc->get_grob_property ("extra-offset");
533         if (gh_pair_p (e))
534           {
535             Offset z = ly_scm2offset (e);
536             z *= Staff_symbol_referencer::staff_space (sc);
537             
538             o += z;
539           }
540         
541         output_molecule (m->get_expr (), o);
542       }
543
544   
545   
546   if (last_line)
547     {
548       output_scheme (scm_list_n (ly_symbol2scm ("stop-last-system"), SCM_UNDEFINED));
549     }
550   else
551     {
552       output_scheme (scm_list_n (ly_symbol2scm ("stop-system"), SCM_UNDEFINED));
553     }
554 }
555
556
557 Link_array<Item> 
558 System::broken_col_range (Item const*l, Item const*r) const
559 {
560   Link_array<Item> ret;
561
562   l = l->get_column ();
563   r = r->get_column ();
564   SCM s = get_grob_property ("columns");
565
566   while (gh_pair_p (s) && ly_car (s) != r->self_scm ())
567     s = ly_cdr (s);
568     
569   if (gh_pair_p (s))
570     s = ly_cdr (s);
571   
572   while (gh_pair_p (s) && ly_car (s) != l->self_scm ())
573     {
574       Paper_column*c = dynamic_cast<Paper_column*> (unsmob_grob (ly_car (s)));
575       if (Item::breakable_b (c) && !c->system_)
576         ret.push (c);
577
578       s = ly_cdr (s);
579     }
580
581   ret.reverse ();
582   return ret;
583 }
584
585 /**
586    Return all columns, but filter out any unused columns , since they might
587    disrupt the spacing problem.
588  */
589 Link_array<Grob>
590 System::columns ()const
591 {
592   Link_array<Grob> acs
593     = Pointer_group_interface__extract_grobs (this, (Grob*) 0, "columns");
594   bool bfound = false;
595   for (int i= acs.size (); i -- ;)
596     {
597       bool brb = Item::breakable_b (acs[i]);
598       bfound = bfound || brb;
599
600       /*
601         the last column should be breakable. Weed out any columns that
602         seem empty. We need to retain breakable columns, in case
603         someone forced a breakpoint.
604       */
605       if (!bfound || !Paper_column::is_used (acs[i]))
606         acs.del (i);
607     }
608   return acs;
609 }
610   
611
612
613
614 ADD_INTERFACE (System,"system-interface",
615   "Super grob, parent of all: "
616 "\n\n"
617 "The columns of a score that form one line.  The toplevel grob.  Any "
618 "grob has a Line_of_score as both X and Y reference point. The "
619 "Paper_score contains one grob of this type. Control enters the "
620 "Grob dependency calculation from this single Line_of_score "
621 "object.",
622   "between-system-string all-elements columns");