2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
22 #include "align-interface.hh"
23 #include "all-font-metrics.hh"
24 #include "axis-group-interface.hh"
25 #include "grob-array.hh"
26 #include "hara-kiri-group-spanner.hh"
27 #include "international.hh"
30 #include "output-def.hh"
31 #include "page-layout-problem.hh"
32 #include "paper-column.hh"
33 #include "paper-score.hh"
34 #include "paper-system.hh"
35 #include "pointer-group-interface.hh"
36 #include "skyline-pair.hh"
37 #include "staff-symbol-referencer.hh"
40 System::System (System
const &src
)
49 System::System (SCM s
)
58 System::init_elements ()
60 SCM scm_arr
= Grob_array::make_array ();
61 all_elements_
= unsmob_grob_array (scm_arr
);
62 all_elements_
->set_ordered (false);
63 set_object ("all-elements", scm_arr
);
67 System::clone () const
69 return new System (*this);
73 System::element_count () const
75 return all_elements_
->size ();
79 System::spanner_count () const
82 for (vsize i
= all_elements_
->size (); i
--;)
83 if (dynamic_cast<Spanner
*> (all_elements_
->grob (i
)))
89 System::typeset_grob (Grob
*elem
)
92 programming_error ("adding element twice");
95 elem
->layout_
= pscore_
->layout ();
96 all_elements_
->add (elem
);
102 System::derived_mark () const
104 if (!all_elements_
->empty ())
106 Grob
**ptr
= &all_elements_
->array_reference ()[0];
107 Grob
**end
= ptr
+ all_elements_
->size ();
110 scm_gc_mark ((*ptr
)->self_scm ());
116 scm_gc_mark (pscore_
->self_scm ());
118 Spanner::derived_mark ();
122 fixup_refpoints (vector
<Grob
*> const &grobs
)
124 for (vsize i
= grobs
.size (); i
--;)
125 grobs
[i
]->fixup_refpoint ();
129 System::do_break_substitution_and_fixup_refpoints ()
131 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
133 Grob
*g
= all_elements_
->grob (i
);
134 if (g
->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
137 Kill no longer needed grobs.
139 Item
*it
= dynamic_cast<Item
*> (g
);
140 if (it
&& Item::is_non_musical (it
))
142 it
->find_prebroken_piece (LEFT
)->suicide ();
143 it
->find_prebroken_piece (RIGHT
)->suicide ();
147 else if (g
->is_live ())
148 g
->do_break_processing ();
152 fixups must be done in broken line_of_scores, because new elements
153 are put over there. */
155 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
157 Grob
*se
= broken_intos_
[i
];
159 extract_grob_set (se
, "all-elements", all_elts
);
160 for (vsize j
= 0; j
< all_elts
.size (); j
++)
162 Grob
*g
= all_elts
[j
];
163 g
->fixup_refpoint ();
166 count
+= all_elts
.size ();
170 needed for doing items.
172 fixup_refpoints (all_elements_
->array ());
174 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
175 all_elements_
->grob (i
)->handle_broken_dependencies ();
177 handle_broken_dependencies ();
179 /* Because the this->get_property (all-elements) contains items in 3
180 versions, handle_broken_dependencies () will leave duplicated
181 items in all-elements. Strictly speaking this is harmless, but
182 it leads to duplicated symbols in the output. uniq makes sure
183 that no duplicates are in the list. */
184 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
186 System
*child
= dynamic_cast<System
*> (broken_intos_
[i
]);
187 child
->all_elements_
->remove_duplicates ();
188 for (vsize j
= 0; j
< child
->all_elements_
->size (); j
++)
190 Grob
*g
= child
->all_elements_
->grob (j
);
192 (void) g
->get_property ("after-line-breaking");
196 if (be_verbose_global
)
197 message (_f ("Element count %d", count
+ element_count ()) + "\n");
201 System::get_broken_system_grobs ()
204 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
205 ret
= scm_cons (broken_intos_
[i
]->self_scm (), ret
);
206 return scm_reverse (ret
);
210 System::get_paper_systems ()
212 SCM lines
= scm_c_make_vector (broken_intos_
.size (), SCM_EOL
);
213 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
215 if (be_verbose_global
)
216 progress_indication ("[");
218 System
*system
= dynamic_cast<System
*> (broken_intos_
[i
]);
220 scm_vector_set_x (lines
, scm_from_int (i
),
221 system
->get_paper_system ());
223 if (be_verbose_global
)
224 progress_indication (to_string (i
) + "]");
230 System::break_into_pieces (vector
<Column_x_positions
> const &breaking
)
232 for (vsize i
= 0; i
< breaking
.size (); i
++)
234 System
*system
= dynamic_cast<System
*> (clone ());
235 system
->rank_
= broken_intos_
.size ();
237 vector
<Grob
*> c (breaking
[i
].cols_
);
238 pscore_
->typeset_system (system
);
240 int st
= Paper_column::get_rank (c
[0]);
241 int end
= Paper_column::get_rank (c
.back ());
242 Interval
iv (pure_height (this, st
, end
));
243 system
->set_property ("pure-Y-extent", ly_interval2scm (iv
));
245 system
->set_bound (LEFT
, c
[0]);
246 system
->set_bound (RIGHT
, c
.back ());
247 SCM system_labels
= SCM_EOL
;
248 for (vsize j
= 0; j
< c
.size (); j
++)
250 c
[j
]->translate_axis (breaking
[i
].config_
[j
], X_AXIS
);
251 dynamic_cast<Paper_column
*> (c
[j
])->set_system (system
);
252 /* collect the column labels */
253 SCM col_labels
= c
[j
]->get_property ("labels");
254 if (scm_is_pair (col_labels
))
255 system_labels
= scm_append (scm_list_2 (col_labels
, system_labels
));
257 system
->set_property ("labels", system_labels
);
259 set_loose_columns (system
, &breaking
[i
]);
260 broken_intos_
.push_back (system
);
265 System::add_column (Paper_column
*p
)
268 Grob_array
*ga
= unsmob_grob_array (me
->get_object ("columns"));
271 SCM scm_ga
= Grob_array::make_array ();
272 me
->set_object ("columns", scm_ga
);
273 ga
= unsmob_grob_array (scm_ga
);
276 p
->set_rank (ga
->size ());
279 Axis_group_interface::add_element (this, p
);
283 System::pre_processing ()
285 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
286 all_elements_
->grob (i
)->discretionary_processing ();
288 if (be_verbose_global
)
289 message (_f ("Grob count %d", element_count ()));
292 order is significant: broken grobs are added to the end of the
293 array, and should be processed before the original is potentially
296 for (vsize i
= all_elements_
->size (); i
--;)
297 all_elements_
->grob (i
)->handle_prebroken_dependencies ();
299 fixup_refpoints (all_elements_
->array ());
301 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
303 Grob
*g
= all_elements_
->grob (i
);
304 (void) g
->get_property ("before-line-breaking");
307 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
309 Grob
*e
= all_elements_
->grob (i
);
310 (void) e
->get_property ("springs-and-rods");
315 System::post_processing ()
317 Interval
iv (extent (this, Y_AXIS
));
319 programming_error ("system with empty extent");
321 translate_axis (-iv
[MAX
], Y_AXIS
);
323 /* Generate all stencils to trigger font loads.
324 This might seem inefficient, but Stencils are cached per grob
327 vector
<Grob
*> all_elts_sorted (all_elements_
->array ());
328 vector_sort (all_elts_sorted
, std::less
<Grob
*> ());
329 uniq (all_elts_sorted
);
330 this->get_stencil ();
331 for (vsize i
= all_elts_sorted
.size (); i
--;)
333 Grob
*g
= all_elts_sorted
[i
];
345 operator< (Layer_entry
const &a
,
346 Layer_entry
const &b
)
348 return a
.layer_
< b
.layer_
;
353 System::get_paper_system ()
360 vector
<Layer_entry
> entries
;
361 for (vsize j
= 0; j
< all_elements_
->size (); j
++)
364 e
.grob_
= all_elements_
->grob (j
);
365 e
.layer_
= robust_scm2int (e
.grob_
->get_property ("layer"), 1);
367 entries
.push_back (e
);
370 vector_sort (entries
, std::less
<Layer_entry
> ());
371 for (vsize j
= 0; j
< entries
.size (); j
++)
373 Grob
*g
= entries
[j
].grob_
;
374 Stencil st
= g
->get_print_stencil ();
376 if (st
.expr () == SCM_EOL
)
380 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
381 o
[Axis (a
)] = g
->relative_coordinate (this, Axis (a
));
383 Offset extra
= robust_scm2offset (g
->get_property ("extra-offset"),
385 * Staff_symbol_referencer::staff_space (g
);
387 /* Must copy the stencil, for we cannot change the stencil
390 st
.translate (o
+ extra
);
392 *tail
= scm_cons (st
.expr (), SCM_EOL
);
393 tail
= SCM_CDRLOC (*tail
);
396 if (Stencil
*me
= get_stencil ())
397 exprs
= scm_cons (me
->expr (), exprs
);
399 Interval
x (extent (this, X_AXIS
));
400 Interval
y (extent (this, Y_AXIS
));
401 Stencil
sys_stencil (Box (x
, y
),
402 scm_cons (ly_symbol2scm ("combine-stencil"),
406 Skyline_pair
*skylines
= Skyline_pair::unsmob (get_property ("vertical-skylines"));
410 = Lookup::points_to_line_stencil (0.1, (*skylines
)[UP
].to_points (X_AXIS
));
412 = Lookup::points_to_line_stencil (0.1, (*skylines
)[DOWN
].to_points (X_AXIS
));
413 sys_stencil
.add_stencil (up
.in_color (255, 0, 0));
414 sys_stencil
.add_stencil (down
.in_color (0, 255, 0));
418 Grob
*left_bound
= this->get_bound (LEFT
);
419 SCM prop_init
= left_bound
->get_property ("line-break-system-details");
420 Prob
*pl
= make_paper_system (prop_init
);
421 paper_system_set_stencil (pl
, sys_stencil
);
423 /* information that the page breaker might need */
424 Grob
*right_bound
= this->get_bound (RIGHT
);
425 pl
->set_property ("vertical-skylines", this->get_property ("vertical-skylines"));
426 pl
->set_property ("page-break-permission", right_bound
->get_property ("page-break-permission"));
427 pl
->set_property ("page-turn-permission", right_bound
->get_property ("page-turn-permission"));
428 pl
->set_property ("page-break-penalty", right_bound
->get_property ("page-break-penalty"));
429 pl
->set_property ("page-turn-penalty", right_bound
->get_property ("page-turn-penalty"));
431 Interval staff_refpoints
;
432 extract_grob_set (this, "spaceable-staves", staves
);
433 for (vsize i
= 0; i
< staves
.size (); i
++)
434 if (staves
[i
]->is_live ())
435 staff_refpoints
.add_point (staves
[i
]->relative_coordinate (this, Y_AXIS
));
437 pl
->set_property ("staff-refpoint-extent", ly_interval2scm (staff_refpoints
));
438 pl
->set_property ("system-grob", this->self_scm ());
440 return pl
->unprotect ();
444 System::broken_col_range (Item
const *left
, Item
const *right
) const
448 left
= left
->get_column ();
449 right
= right
->get_column ();
452 extract_grob_set (this, "columns", cols
);
454 vsize i
= Paper_column::get_rank (left
);
455 int end_rank
= Paper_column::get_rank (right
);
456 if (i
< cols
.size ())
459 while (i
< cols
.size ()
460 && Paper_column::get_rank (cols
[i
]) < end_rank
)
462 Paper_column
*c
= dynamic_cast<Paper_column
*> (cols
[i
]);
463 if (Paper_column::is_breakable (c
) && !c
->get_system ())
472 /** Return all columns, but filter out any unused columns , since they might
473 disrupt the spacing problem. */
475 System::used_columns () const
477 extract_grob_set (this, "columns", ro_columns
);
479 int last_breakable
= ro_columns
.size ();
481 while (last_breakable
--)
483 if (Paper_column::is_breakable (ro_columns
[last_breakable
]))
487 vector
<Grob
*> columns
;
488 for (int i
= 0; i
<= last_breakable
; i
++)
490 if (Paper_column::is_used (ro_columns
[i
]))
491 columns
.push_back (ro_columns
[i
]);
498 System::column (vsize which
) const
500 extract_grob_set (this, "columns", columns
);
501 if (which
>= columns
.size ())
504 return dynamic_cast<Paper_column
*> (columns
[which
]);
508 System::paper_score () const
514 System::get_rank () const
520 get_root_system (Grob
*me
)
522 Grob
*system_grob
= me
;
524 while (system_grob
->get_parent (Y_AXIS
))
525 system_grob
= system_grob
->get_parent (Y_AXIS
);
527 return dynamic_cast<System
*> (system_grob
);
531 System::get_vertical_alignment ()
533 extract_grob_set (this, "elements", elts
);
535 for (vsize i
= 0; i
< elts
.size (); i
++)
536 if (Align_interface::has_interface (elts
[i
]))
539 programming_error ("found multiple vertical alignments in this system");
544 programming_error ("didn't find a vertical alignment in this system");
548 // Finds the furthest staff in the given direction whose x-extent
549 // overlaps with the given interval.
551 System::get_extremal_staff (Direction dir
, Interval
const &iv
)
553 Grob
*align
= get_vertical_alignment ();
557 extract_grob_set (align
, "elements", elts
);
558 vsize start
= (dir
== UP
) ? 0 : elts
.size () - 1;
559 vsize end
= (dir
== UP
) ? elts
.size () : VPOS
;
560 for (vsize i
= start
; i
!= end
; i
+= dir
)
562 if (Hara_kiri_group_spanner::has_interface (elts
[i
]))
563 Hara_kiri_group_spanner::consider_suicide (elts
[i
]);
565 Interval intersection
= elts
[i
]->extent (this, X_AXIS
);
566 intersection
.intersect (iv
);
567 if (elts
[i
]->is_live () && !intersection
.is_empty ())
574 System::part_of_line_pure_height (vsize start
, vsize end
, bool begin
)
576 Grob
*alignment
= get_vertical_alignment ();
579 programming_error("system does not have a vertical alignment");
582 extract_grob_set (alignment
, "elements", staves
);
583 vector
<Real
> offsets
= Align_interface::get_minimum_translations (alignment
, staves
, Y_AXIS
, true, start
, end
);
586 for (vsize i
= 0; i
< staves
.size(); ++i
)
588 Interval iv
= begin
?
589 Axis_group_interface::begin_of_line_pure_height (staves
[i
], start
) :
590 Axis_group_interface::rest_of_line_pure_height (staves
[i
], start
, end
);
591 if (i
<offsets
.size())
592 iv
.translate (offsets
[i
]);
596 Interval other_elements
= begin
?
597 Axis_group_interface::begin_of_line_pure_height (this, start
) :
598 Axis_group_interface::rest_of_line_pure_height (this, start
, end
);
600 ret
.unite (other_elements
);
606 System::begin_of_line_pure_height (vsize start
, vsize end
)
608 return part_of_line_pure_height (start
, end
, true);
612 System::rest_of_line_pure_height (vsize start
, vsize end
)
614 return part_of_line_pure_height (start
, end
, false);
617 // This differs from Axis_group_interface::calc_pure_relevant_grobs
618 // because here, we are only interested in those few elements that aren't
619 // descended from VerticalAlignment (ie. things like RehearsalMark, BarLine).
620 MAKE_SCHEME_CALLBACK (System
, calc_pure_relevant_grobs
, 1);
622 System::calc_pure_relevant_grobs (SCM smob
)
624 Grob
*me
= unsmob_grob (smob
);
626 extract_grob_set (me
, "elements", elts
);
627 vector
<Grob
*> relevant_grobs
;
628 SCM pure_relevant_p
= ly_lily_module_constant ("pure-relevant?");
630 for (vsize i
= 0; i
< elts
.size (); ++i
)
632 if (!Axis_group_interface::has_interface (elts
[i
])
633 && to_boolean (scm_apply_1 (pure_relevant_p
, elts
[i
]->self_scm (), SCM_EOL
)))
634 relevant_grobs
.push_back (elts
[i
]);
637 SCM grobs_scm
= Grob_array::make_array ();
639 unsmob_grob_array (grobs_scm
)->set_array (relevant_grobs
);
643 MAKE_SCHEME_CALLBACK (System
, height
, 1);
645 System::height (SCM smob
)
647 return Axis_group_interface::height (smob
);
650 MAKE_SCHEME_CALLBACK (System
, calc_pure_height
, 3);
652 System::calc_pure_height (SCM smob
, SCM start_scm
, SCM end_scm
)
654 System
*me
= dynamic_cast<System
*> (unsmob_grob (smob
));
655 int start
= scm_to_int (start_scm
);
656 int end
= scm_to_int (end_scm
);
658 Interval begin
= me
->begin_of_line_pure_height (start
, end
);
659 Interval rest
= me
->rest_of_line_pure_height (start
, end
);
662 return ly_interval2scm (begin
);
665 ADD_INTERFACE (System
,
666 "This is the top-level object: Each object in a score"
667 " ultimately has a @code{System} object as its X and"
677 "skyline-horizontal-padding "