2 system.cc -- implement System
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
11 #include "align-interface.hh"
12 #include "all-font-metrics.hh"
13 #include "axis-group-interface.hh"
14 #include "grob-array.hh"
15 #include "hara-kiri-group-spanner.hh"
16 #include "international.hh"
19 #include "output-def.hh"
20 #include "page-layout-problem.hh"
21 #include "paper-column.hh"
22 #include "paper-score.hh"
23 #include "paper-system.hh"
24 #include "pointer-group-interface.hh"
25 #include "skyline-pair.hh"
26 #include "staff-symbol-referencer.hh"
29 System::System (System
const &src
)
38 System::System (SCM s
)
47 System::init_elements ()
49 SCM scm_arr
= Grob_array::make_array ();
50 all_elements_
= unsmob_grob_array (scm_arr
);
51 all_elements_
->set_ordered (false);
52 set_object ("all-elements", scm_arr
);
56 System::clone () const
58 return new System (*this);
62 System::element_count () const
64 return all_elements_
->size ();
68 System::spanner_count () const
71 for (vsize i
= all_elements_
->size (); i
--;)
72 if (dynamic_cast<Spanner
*> (all_elements_
->grob (i
)))
78 System::typeset_grob (Grob
*elem
)
81 programming_error ("adding element twice");
84 elem
->layout_
= pscore_
->layout ();
85 all_elements_
->add (elem
);
91 System::derived_mark () const
93 if (!all_elements_
->empty ())
95 Grob
**ptr
= &all_elements_
->array_reference ()[0];
96 Grob
**end
= ptr
+ all_elements_
->size ();
99 scm_gc_mark ((*ptr
)->self_scm ());
105 scm_gc_mark (pscore_
->self_scm ());
107 Spanner::derived_mark ();
111 fixup_refpoints (vector
<Grob
*> const &grobs
)
113 for (vsize i
= grobs
.size (); i
--;)
114 grobs
[i
]->fixup_refpoint ();
118 System::do_break_substitution_and_fixup_refpoints ()
120 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
122 Grob
*g
= all_elements_
->grob (i
);
123 if (g
->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
126 Kill no longer needed grobs.
128 Item
*it
= dynamic_cast<Item
*> (g
);
129 if (it
&& Item::is_non_musical (it
))
131 it
->find_prebroken_piece (LEFT
)->suicide ();
132 it
->find_prebroken_piece (RIGHT
)->suicide ();
136 else if (g
->is_live ())
137 g
->do_break_processing ();
141 fixups must be done in broken line_of_scores, because new elements
142 are put over there. */
144 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
146 Grob
*se
= broken_intos_
[i
];
148 extract_grob_set (se
, "all-elements", all_elts
);
149 for (vsize j
= 0; j
< all_elts
.size (); j
++)
151 Grob
*g
= all_elts
[j
];
152 g
->fixup_refpoint ();
155 count
+= all_elts
.size ();
159 needed for doing items.
161 fixup_refpoints (all_elements_
->array ());
163 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
164 all_elements_
->grob (i
)->handle_broken_dependencies ();
166 handle_broken_dependencies ();
168 /* Because the this->get_property (all-elements) contains items in 3
169 versions, handle_broken_dependencies () will leave duplicated
170 items in all-elements. Strictly speaking this is harmless, but
171 it leads to duplicated symbols in the output. uniq makes sure
172 that no duplicates are in the list. */
173 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
175 System
*child
= dynamic_cast<System
*> (broken_intos_
[i
]);
176 child
->all_elements_
->remove_duplicates ();
177 for (vsize j
= 0; j
< child
->all_elements_
->size (); j
++)
179 Grob
*g
= child
->all_elements_
->grob (j
);
181 (void) g
->get_property ("after-line-breaking");
185 if (be_verbose_global
)
186 message (_f ("Element count %d", count
+ element_count ()) + "\n");
190 System::get_broken_system_grobs ()
193 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
194 ret
= scm_cons (broken_intos_
[i
]->self_scm (), ret
);
195 return scm_reverse (ret
);
199 System::get_paper_systems ()
201 SCM lines
= scm_c_make_vector (broken_intos_
.size (), SCM_EOL
);
202 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
204 if (be_verbose_global
)
205 progress_indication ("[");
207 System
*system
= dynamic_cast<System
*> (broken_intos_
[i
]);
209 scm_vector_set_x (lines
, scm_from_int (i
),
210 system
->get_paper_system ());
212 if (be_verbose_global
)
213 progress_indication (to_string (i
) + "]");
219 System::break_into_pieces (vector
<Column_x_positions
> const &breaking
)
221 for (vsize i
= 0; i
< breaking
.size (); i
++)
223 System
*system
= dynamic_cast<System
*> (clone ());
224 system
->rank_
= broken_intos_
.size ();
226 vector
<Grob
*> c (breaking
[i
].cols_
);
227 pscore_
->typeset_system (system
);
229 int st
= Paper_column::get_rank (c
[0]);
230 int end
= Paper_column::get_rank (c
.back ());
231 Interval
iv (pure_height (this, st
, end
));
232 system
->set_property ("pure-Y-extent", ly_interval2scm (iv
));
234 system
->set_bound (LEFT
, c
[0]);
235 system
->set_bound (RIGHT
, c
.back ());
236 SCM system_labels
= SCM_EOL
;
237 for (vsize j
= 0; j
< c
.size (); j
++)
239 c
[j
]->translate_axis (breaking
[i
].config_
[j
], X_AXIS
);
240 dynamic_cast<Paper_column
*> (c
[j
])->system_
= system
;
241 /* collect the column labels */
242 SCM col_labels
= c
[j
]->get_property ("labels");
243 if (scm_is_pair (col_labels
))
244 system_labels
= scm_append (scm_list_2 (col_labels
, system_labels
));
246 system
->set_property ("labels", system_labels
);
248 set_loose_columns (system
, &breaking
[i
]);
249 broken_intos_
.push_back (system
);
254 System::add_column (Paper_column
*p
)
257 Grob_array
*ga
= unsmob_grob_array (me
->get_object ("columns"));
260 SCM scm_ga
= Grob_array::make_array ();
261 me
->set_object ("columns", scm_ga
);
262 ga
= unsmob_grob_array (scm_ga
);
265 p
->rank_
= ga
->size ();
268 Axis_group_interface::add_element (this, p
);
272 System::pre_processing ()
274 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
275 all_elements_
->grob (i
)->discretionary_processing ();
277 if (be_verbose_global
)
278 message (_f ("Grob count %d", element_count ()));
281 order is significant: broken grobs are added to the end of the
282 array, and should be processed before the original is potentially
285 for (vsize i
= all_elements_
->size (); i
--;)
286 all_elements_
->grob (i
)->handle_prebroken_dependencies ();
288 fixup_refpoints (all_elements_
->array ());
290 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
292 Grob
*g
= all_elements_
->grob (i
);
293 (void) g
->get_property ("before-line-breaking");
296 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
298 Grob
*e
= all_elements_
->grob (i
);
299 (void) e
->get_property ("springs-and-rods");
304 System::post_processing ()
306 Interval
iv (extent (this, Y_AXIS
));
308 programming_error ("system with empty extent");
310 translate_axis (-iv
[MAX
], Y_AXIS
);
312 /* Generate all stencils to trigger font loads.
313 This might seem inefficient, but Stencils are cached per grob
316 vector
<Grob
*> all_elts_sorted (all_elements_
->array ());
317 vector_sort (all_elts_sorted
, std::less
<Grob
*> ());
318 uniq (all_elts_sorted
);
319 this->get_stencil ();
320 for (vsize i
= all_elts_sorted
.size (); i
--;)
322 Grob
*g
= all_elts_sorted
[i
];
334 operator< (Layer_entry
const &a
,
335 Layer_entry
const &b
)
337 return a
.layer_
< b
.layer_
;
342 System::get_paper_system ()
349 vector
<Layer_entry
> entries
;
350 for (vsize j
= 0; j
< all_elements_
->size (); j
++)
353 e
.grob_
= all_elements_
->grob (j
);
354 e
.layer_
= robust_scm2int (e
.grob_
->get_property ("layer"), 1);
356 entries
.push_back (e
);
359 vector_sort (entries
, std::less
<Layer_entry
> ());
360 for (vsize j
= 0; j
< entries
.size (); j
++)
362 Grob
*g
= entries
[j
].grob_
;
363 Stencil st
= g
->get_print_stencil ();
365 if (st
.expr () == SCM_EOL
)
369 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
370 o
[Axis (a
)] = g
->relative_coordinate (this, Axis (a
));
372 Offset extra
= robust_scm2offset (g
->get_property ("extra-offset"),
374 * Staff_symbol_referencer::staff_space (g
);
376 /* Must copy the stencil, for we cannot change the stencil
379 st
.translate (o
+ extra
);
381 *tail
= scm_cons (st
.expr (), SCM_EOL
);
382 tail
= SCM_CDRLOC (*tail
);
385 if (Stencil
*me
= get_stencil ())
386 exprs
= scm_cons (me
->expr (), exprs
);
388 Interval
x (extent (this, X_AXIS
));
389 Interval
y (extent (this, Y_AXIS
));
390 Stencil
sys_stencil (Box (x
, y
),
391 scm_cons (ly_symbol2scm ("combine-stencil"),
395 Skyline_pair
*skylines
= Skyline_pair::unsmob (get_property ("vertical-skylines"));
399 = Lookup::points_to_line_stencil (0.1, (*skylines
)[UP
].to_points (X_AXIS
));
401 = Lookup::points_to_line_stencil (0.1, (*skylines
)[DOWN
].to_points (X_AXIS
));
402 sys_stencil
.add_stencil (up
.in_color (255, 0, 0));
403 sys_stencil
.add_stencil (down
.in_color (0, 255, 0));
407 Grob
*left_bound
= this->get_bound (LEFT
);
408 SCM prop_init
= left_bound
->get_property ("line-break-system-details");
409 Prob
*pl
= make_paper_system (prop_init
);
410 paper_system_set_stencil (pl
, sys_stencil
);
412 /* information that the page breaker might need */
413 Grob
*right_bound
= this->get_bound (RIGHT
);
414 pl
->set_property ("vertical-skylines", this->get_property ("vertical-skylines"));
415 pl
->set_property ("page-break-permission", right_bound
->get_property ("page-break-permission"));
416 pl
->set_property ("page-turn-permission", right_bound
->get_property ("page-turn-permission"));
417 pl
->set_property ("page-break-penalty", right_bound
->get_property ("page-break-penalty"));
418 pl
->set_property ("page-turn-penalty", right_bound
->get_property ("page-turn-penalty"));
420 Interval staff_refpoints
;
421 extract_grob_set (this, "spaceable-staves", staves
);
422 for (vsize i
= 0; i
< staves
.size (); i
++)
423 if (staves
[i
]->is_live ())
424 staff_refpoints
.add_point (staves
[i
]->relative_coordinate (this, Y_AXIS
));
426 pl
->set_property ("staff-refpoint-extent", ly_interval2scm (staff_refpoints
));
427 pl
->set_property ("system-grob", this->self_scm ());
429 return pl
->unprotect ();
433 System::broken_col_range (Item
const *left
, Item
const *right
) const
437 left
= left
->get_column ();
438 right
= right
->get_column ();
441 extract_grob_set (this, "columns", cols
);
443 vsize i
= Paper_column::get_rank (left
);
444 int end_rank
= Paper_column::get_rank (right
);
445 if (i
< cols
.size ())
448 while (i
< cols
.size ()
449 && Paper_column::get_rank (cols
[i
]) < end_rank
)
451 Paper_column
*c
= dynamic_cast<Paper_column
*> (cols
[i
]);
452 if (Paper_column::is_breakable (c
) && !c
->system_
)
461 /** Return all columns, but filter out any unused columns , since they might
462 disrupt the spacing problem. */
464 System::used_columns () const
466 extract_grob_set (this, "columns", ro_columns
);
468 int last_breakable
= ro_columns
.size ();
470 while (last_breakable
--)
472 if (Paper_column::is_breakable (ro_columns
[last_breakable
]))
476 vector
<Grob
*> columns
;
477 for (int i
= 0; i
<= last_breakable
; i
++)
479 if (Paper_column::is_used (ro_columns
[i
]))
480 columns
.push_back (ro_columns
[i
]);
487 System::column (vsize which
) const
489 extract_grob_set (this, "columns", columns
);
490 if (which
>= columns
.size ())
493 return dynamic_cast<Paper_column
*> (columns
[which
]);
497 System::paper_score () const
503 System::get_rank () const
509 get_root_system (Grob
*me
)
511 Grob
*system_grob
= me
;
513 while (system_grob
->get_parent (Y_AXIS
))
514 system_grob
= system_grob
->get_parent (Y_AXIS
);
516 return dynamic_cast<System
*> (system_grob
);
520 System::get_vertical_alignment ()
522 extract_grob_set (this, "elements", elts
);
524 for (vsize i
= 0; i
< elts
.size (); i
++)
525 if (Align_interface::has_interface (elts
[i
]))
528 programming_error ("found multiple vertical alignments in this system");
533 programming_error ("didn't find a vertical alignment in this system");
537 // Finds the furthest staff in the given direction whose x-extent
538 // overlaps with the given interval.
540 System::get_extremal_staff (Direction dir
, Interval
const &iv
)
542 Grob
*align
= get_vertical_alignment ();
546 extract_grob_set (align
, "elements", elts
);
547 vsize start
= (dir
== UP
) ? 0 : elts
.size () - 1;
548 vsize end
= (dir
== UP
) ? elts
.size () : VPOS
;
549 for (vsize i
= start
; i
!= end
; i
+= dir
)
551 if (Hara_kiri_group_spanner::has_interface (elts
[i
]))
552 Hara_kiri_group_spanner::consider_suicide (elts
[i
]);
554 Interval intersection
= elts
[i
]->extent (this, X_AXIS
);
555 intersection
.intersect (iv
);
556 if (elts
[i
]->is_live () && !intersection
.is_empty ())
562 ADD_INTERFACE (System
,
563 "This is the top-level object: Each object in a score"
564 " ultimately has a @code{System} object as its X and"
574 "skyline-horizontal-padding "