]> git.donarmstrong.com Git - lilypond.git/blob - lily/system.cc
release: 1.5.47
[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--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "input-smob.hh"
10 #include "axis-group-interface.hh"
11 #include "debug.hh"
12 #include "line-of-score.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
24 // todo: use map.
25 void
26 fixup_refpoints (SCM s)
27 {
28   for (; gh_pair_p (s); s = ly_cdr (s))
29     {
30       Grob::fixup_refpoint (ly_car (s));
31     }
32 }
33
34
35 System::System (SCM s)
36   : Spanner (s)
37 {
38   rank_i_ = 0;
39 }
40
41 int
42 System::element_count () const
43 {
44   return scm_ilength (get_grob_property ("all-elements"));
45 }
46
47 void
48 System::typeset_grob (Grob * elem_p)
49 {
50   elem_p->pscore_l_ = pscore_l_;
51   Pointer_group_interface::add_grob (this, ly_symbol2scm ("all-elements"),elem_p);
52   scm_gc_unprotect_object (elem_p->self_scm ());
53 }
54
55 void
56 System::output_lines ()
57 {
58   for (SCM s = get_grob_property ("all-elements");
59        gh_pair_p (s); s = ly_cdr (s))
60     {
61       unsmob_grob (ly_car (s))->do_break_processing ();
62     }
63
64   /*
65     fixups must be done in broken line_of_scores, because new elements
66     are put over there.  */
67   int count = 0;
68   for (int i=0; i < broken_into_l_arr_.size (); i++)
69     {
70       Grob *se = broken_into_l_arr_[i];
71       SCM all = se->get_grob_property ("all-elements");
72       for (SCM s = all; gh_pair_p (s); s = ly_cdr (s))
73         {
74           fixup_refpoint (ly_car (s));
75         }
76       count += scm_ilength (all);
77     }
78   
79   /*
80     needed for doing items.
81    */
82   fixup_refpoints (get_grob_property ("all-elements"));
83
84   
85   for (SCM s = get_grob_property ("all-elements");
86        gh_pair_p (s); s = ly_cdr (s))
87     {
88       unsmob_grob (ly_car (s))->handle_broken_dependencies ();
89     }
90   handle_broken_dependencies ();
91
92   if (verbose_global_b)
93     progress_indication (_f ("Element count %d.",  count + element_count ()));
94
95   
96   for (int i=0; i < broken_into_l_arr_.size (); i++)
97     {
98       System *line_l = dynamic_cast<System*> (broken_into_l_arr_[i]);
99
100       if (verbose_global_b)
101         progress_indication ("[");
102       line_l->post_processing (i+1 == broken_into_l_arr_.size ());
103
104       if (verbose_global_b)
105         {
106           progress_indication (to_str (i));
107           progress_indication ("]");
108         }
109
110       if (i < broken_into_l_arr_.size () - 1)
111         {
112           SCM lastcol =  ly_car (line_l->get_grob_property ("columns"));
113           Grob*  e = unsmob_grob (lastcol);
114
115           SCM between = ly_symbol2scm ("between-system-string");
116           SCM inter = e->internal_get_grob_property (between);
117           if (gh_string_p (inter))
118             {
119               pscore_l_->outputter_l_
120                 ->output_scheme (scm_list_n (between, 
121                                              inter, SCM_UNDEFINED));          
122             }
123         }
124     }
125 }
126
127
128
129
130 /*
131   Find the loose columns in POSNS, and drape them around the columns
132   specified in BETWEEN-COLS.  */
133 void
134 set_loose_columns (System* which, Column_x_positions const *posns)
135 {
136   for (int i = 0; i < posns->loose_cols_.size (); i++)
137     {
138       int divide_over = 1;
139       Item *loose = dynamic_cast<Item*> (posns->loose_cols_[i]);
140       Paper_column* col = dynamic_cast<Paper_column*> (loose);
141       
142       if (col->line_l_)
143         continue;
144
145       
146       Item * left = 0;
147       Item * right = 0;
148       do
149         {
150           SCM between = loose->get_grob_property ("between-cols");
151           if (!gh_pair_p (between))
152             break;
153
154
155           Item * l=dynamic_cast<Item*> (unsmob_grob (ly_car (between)));
156           Item * r=dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
157
158           if (!(l && r))
159             break ;
160           
161           if (!left && l)
162             {
163               left = l->column_l ();
164             }
165
166           divide_over ++;
167
168           loose = right = r->column_l ();
169         }
170       while (1);
171       
172     
173 #if 0
174       Real rx = right->relative_coordinate (right->get_parent (X_AXIS), X_AXIS);
175       Real lx = left->relative_coordinate (left->get_parent (X_AXIS), X_AXIS);
176
177       /*
178         divide space equally over loose columns.
179        */
180       int j = 1;
181       loose = col;
182       while (1)
183         {
184           SCM between = loose->get_grob_property ("between-cols");
185           if (!gh_pair_p (between))
186             break;
187
188           Paper_column *thiscol = dynamic_cast<Paper_column*> (loose);
189
190           thiscol->line_l_ = which;
191           thiscol->translate_axis (lx + j*(rx - lx)/divide_over, X_AXIS);
192
193           j ++; 
194           loose = dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
195         }
196 #else
197       /*
198         We divide the remaining space of the column over the left and
199         right side. At the moment, we  
200         
201       */
202       Grob * common = right->common_refpoint (left, X_AXIS);
203       
204       Real rx = right->extent(common, X_AXIS)[LEFT];
205       Real lx =  left->extent(common, X_AXIS)[RIGHT];
206       Real total_dx = rx - lx;
207       Interval cval =col->extent (col, X_AXIS);
208
209       /*
210         
211         We put it in the middle. This is not an ideal solution -- the
212         break alignment code inserts a fixed space before the clef
213         (about 1 SS), while the space following the clef is
214         flexible. In tight situations, the clef will almost be on top
215         of the following note. 
216         
217       */
218       Real dx = rx-lx - cval.length ();
219       if (total_dx < 2* cval.length ())
220         {
221           /*
222             todo: this is discontinuous. I'm too tired to
223             invent a sliding mechanism. Duh.
224
225             TODO.
226            */
227           dx *= 0.25;
228         }
229       else
230         dx *= 0.5;
231
232       col->line_l_ = which;
233       col->translate_axis (lx + dx - cval[LEFT], X_AXIS); 
234 #endif
235     }
236 }
237
238 // const?
239 void
240 System::break_into_pieces (Array<Column_x_positions> const &breaking)
241 {
242   for (int i=0; i < breaking.size (); i++)
243     {
244       System *line_l = dynamic_cast <System*> (clone ());
245       line_l->rank_i_ = i;
246       //      line_l->set_immutable_grob_property ("rank", gh_int2scm (i));
247       Link_array<Grob> c (breaking[i].cols_);
248       pscore_l_->typeset_line (line_l);
249       
250       line_l->set_bound (LEFT,c[0]);
251       line_l->set_bound (RIGHT,c.top ());
252       for (int j=0; j < c.size (); j++)
253         {
254           c[j]->translate_axis (breaking[i].config_[j],X_AXIS);
255           dynamic_cast<Paper_column*> (c[j])->line_l_ = line_l;
256         }
257       set_loose_columns (line_l, &breaking[i]);
258       broken_into_l_arr_.push (line_l);
259     }
260 }
261
262
263 #define GLOBAL_SYMBOL(cname, name)  \
264 SCM cname ;                                     \
265 void \
266 cname ## _init_func ()                          \
267 {                                               \
268   cname = ly_symbol2scm (name);                 \
269   scm_permanent_object (cname);                 \
270 }                                               \
271 ADD_SCM_INIT_FUNC (cname,cname ## _init_func);\
272
273
274 GLOBAL_SYMBOL (offset_sym , "translate-molecule");
275 GLOBAL_SYMBOL (placebox_sym , "placebox");
276 GLOBAL_SYMBOL (combine_sym , "combine-molecule");
277
278
279
280 void
281 System::output_molecule (SCM expr, Offset o)
282 {
283
284   while (1)
285     {
286       if (!gh_pair_p (expr))
287         return;
288   
289       SCM head =ly_car (expr);
290       if (unsmob_input (head))
291         {
292           Input * ip = unsmob_input (head);
293       
294
295           pscore_l_->outputter_l_->output_scheme (scm_list_n (ly_symbol2scm ("define-origin"),
296                                                            ly_str02scm (ip->file_str ().ch_C ()),
297                                                            gh_int2scm (ip->line_number ()),
298                                                            gh_int2scm (ip->column_number ()),
299                                                            SCM_UNDEFINED));
300           expr = ly_cadr (expr);
301         }
302       else  if (head ==  ly_symbol2scm ("no-origin"))
303         {
304           pscore_l_->outputter_l_->output_scheme (scm_list_n (head, SCM_UNDEFINED));
305           expr = ly_cadr (expr);
306         }
307       else if (head == ly_symbol2scm ("translate-molecule"))
308         {
309           o += ly_scm2offset (ly_cadr (expr));
310           expr = ly_caddr (expr);
311         }
312       else if (head == ly_symbol2scm ("combine-molecule"))
313         {
314           output_molecule (ly_cadr (expr), o);
315           expr = ly_caddr (expr);
316         }
317       else
318         {
319           pscore_l_->outputter_l_->
320             output_scheme (scm_list_n (placebox_sym,
321                                     gh_double2scm (o[X_AXIS]),
322                                     gh_double2scm (o[Y_AXIS]),
323                                     expr,
324                                     SCM_UNDEFINED));
325
326           return;
327         }
328     }
329 }
330
331 void
332 System::output_scheme (SCM s)
333 {
334   pscore_l_->outputter_l_->output_scheme (s);
335 }
336
337 void
338 System::add_column (Paper_column*p)
339 {
340   Grob *me = this;
341   SCM cs = me->get_grob_property ("columns");
342   Grob * prev =  gh_pair_p (cs) ? unsmob_grob (ly_car (cs)) : 0;
343
344   p->rank_i_ = prev ? Paper_column::rank_i (prev) + 1 : 0; 
345
346   me->set_grob_property ("columns",  gh_cons (p->self_scm (), cs));
347
348   Axis_group_interface::add_element (me, p);
349 }
350
351
352
353 /*
354   TODO: use scm_map iso. for loops.
355  */
356 void
357 System::pre_processing ()
358 {
359   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
360     unsmob_grob (ly_car (s))->discretionary_processing ();
361
362   if (verbose_global_b)
363     progress_indication (_f ("Element count %d ",  element_count ()));
364
365   
366   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
367     unsmob_grob (ly_car (s))->handle_prebroken_dependencies ();
368   
369   fixup_refpoints (get_grob_property ("all-elements"));
370   
371   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
372     {
373       Grob* sc = unsmob_grob (ly_car (s));
374       sc->calculate_dependencies (PRECALCED, PRECALCING, ly_symbol2scm ("before-line-breaking-callback"));
375     }
376   
377   progress_indication ("\n" + _ ("Calculating column positions...") + " ");
378   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
379     {
380       Grob * e = unsmob_grob (ly_car (s));
381       SCM proc = e->get_grob_property ("spacing-procedure");
382       if (gh_procedure_p (proc))
383         gh_call1 (proc, e->self_scm ());
384     }
385 }
386
387 void
388 System::post_processing (bool last_line)
389 {
390   for (SCM s = get_grob_property ("all-elements");
391        gh_pair_p (s); s = ly_cdr (s))
392     {
393       Grob* sc = unsmob_grob (ly_car (s));
394       sc->calculate_dependencies (POSTCALCED, POSTCALCING,
395                                   ly_symbol2scm ("after-line-breaking-callback"));
396     }
397
398   Interval i (extent (this, Y_AXIS));
399   if (i.empty_b ())
400     programming_error ("Huh?  Empty System?");
401   else
402     translate_axis (- i[MAX], Y_AXIS);
403
404   Real height = i.length ();
405   if (height > 50 CM)
406     {
407       programming_error ("Improbable system height");
408       height = 50 CM;
409     }
410
411   /*
412     generate all molecules  to trigger all font loads.
413
414     (ugh. This is not very memory efficient.)  */
415   this->get_molecule();
416   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
417     {
418       unsmob_grob (ly_car (s))->get_molecule ();
419     }
420   /*
421     font defs;
422    */
423   SCM font_names = ly_quote_scm (paper_l ()->font_descriptions ());  
424   output_scheme (scm_list_n (ly_symbol2scm ("define-fonts"),
425                              font_names,
426                              SCM_UNDEFINED));
427
428   /*
429     line preamble.
430    */
431   output_scheme (scm_list_n (ly_symbol2scm ("start-line"),
432                           gh_double2scm (height),
433                           SCM_UNDEFINED));
434   
435   /* Output elements in three layers, 0, 1, 2.
436      The default layer is 1. */
437
438   {
439     Molecule *m = this->get_molecule();
440     if (m)
441       output_molecule (m->get_expr (), Offset(0,0));
442   }
443   
444   for (int i = 0; i < 3; i++)
445     for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s);
446          s = ly_cdr (s))
447       {
448         Grob *sc = unsmob_grob (ly_car (s));
449         Molecule *m = sc->get_molecule ();
450         if (!m)
451           continue;
452         
453         SCM s = sc->get_grob_property ("layer");
454         int layer = gh_number_p (s) ? gh_scm2int (s) : 1;
455         if (layer != i)
456           continue;
457         
458         Offset o (sc->relative_coordinate (this, X_AXIS),
459                   sc->relative_coordinate (this, Y_AXIS));
460         
461         SCM e = sc->get_grob_property ("extra-offset");
462         if (gh_pair_p (e))
463           {
464             o[X_AXIS] += gh_scm2double (ly_car (e));
465             o[Y_AXIS] += gh_scm2double (ly_cdr (e));      
466           }
467         
468         output_molecule (m->get_expr (), o);
469       }
470
471   
472   
473   if (last_line)
474     {
475       output_scheme (scm_list_n (ly_symbol2scm ("stop-last-line"), SCM_UNDEFINED));
476     }
477   else
478     {
479       output_scheme (scm_list_n (ly_symbol2scm ("stop-line"), SCM_UNDEFINED));
480     }
481 }
482
483
484 Link_array<Item> 
485 System::broken_col_range (Item const*l, Item const*r) const
486 {
487   Link_array<Item> ret;
488
489   l = l->column_l ();
490   r = r->column_l ();
491   SCM s = get_grob_property ("columns");
492
493   while (gh_pair_p (s) && ly_car (s) != r->self_scm ())
494     s = ly_cdr (s);
495     
496   if (gh_pair_p (s))
497     s = ly_cdr (s);
498   
499   while (gh_pair_p (s) && ly_car (s) != l->self_scm ())
500     {
501       Paper_column*c = dynamic_cast<Paper_column*> (unsmob_grob (ly_car (s)));
502       if (Item::breakable_b (c) && !c->line_l_)
503         ret.push (c);
504
505       s = ly_cdr (s);
506     }
507
508   ret.reverse ();
509   return ret;
510 }
511
512 /**
513    Return all columns, but filter out any unused columns , since they might
514    disrupt the spacing problem.
515  */
516 Link_array<Grob>
517 System::column_l_arr ()const
518 {
519   Link_array<Grob> acs
520     = Pointer_group_interface__extract_grobs (this, (Grob*) 0, "columns");
521   bool bfound = false;
522   for (int i= acs.size (); i -- ;)
523     {
524       bool brb = Item::breakable_b (acs[i]);
525       bfound = bfound || brb;
526
527       /*
528         the last column should be breakable. Weed out any columns that
529         seem empty. We need to retain breakable columns, in case
530         someone forced a breakpoint.
531       */
532       if (!bfound || !Paper_column::used_b (acs[i]))
533         acs.del (i);
534     }
535   return acs;
536 }
537   
538
539
540
541 ADD_INTERFACE (System,"system-interface",
542   "Super grob, parent of all:
543
544 The columns of a score that form one line.  The toplevel grob.  Any
545 grob has a Line_of_score as both X and Y reference point. The
546 Paper_score contains one grob of this type. Control enters the
547 Grob dependency calculation from this single Line_of_score
548 object.",
549   "between-system-string spacing-procedure before-line-breaking-callback after-line-breaking-callback all-elements columns");