2 system.cc -- implement System
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include "axis-group-interface.hh"
13 #include "paper-column.hh"
14 #include "paper-def.hh"
15 #include "paper-outputter.hh"
16 #include "paper-score.hh"
20 #include "all-font-metrics.hh"
21 #include "spacing-interface.hh"
22 #include "staff-symbol-referencer.hh"
23 #include "paper-book.hh"
24 #include "paper-line.hh"
26 System::System (SCM s
)
33 System::element_count () const
35 return scm_ilength (get_property ("all-elements"));
39 System::spanner_count () const
42 for (SCM s
= get_property ("all-elements");
43 ly_pair_p (s
); s
= ly_cdr (s
))
45 if (dynamic_cast<Spanner
*> (unsmob_grob (ly_car (s
))))
54 scm_default_compare (const void * a
, const void *b
)
68 modify L in place: sort it
73 int len
= scm_ilength (l
);
74 SCM
* arr
= new SCM
[len
];
76 for (SCM s
= l
; SCM_NNULLP (s
); s
= SCM_CDR (s
))
77 arr
[k
++] = SCM_CAR (s
);
80 qsort (arr
, len
, sizeof (SCM
), &scm_default_compare
);
85 for (int i
= 0; i
< len
; i
++)
87 if (i
&& arr
[i
] == arr
[i
-1])
90 SCM_SETCAR (*tail
, arr
[i
]);
91 tail
= SCM_CDRLOC(*tail
);
101 System::typeset_grob (Grob
* elem
)
104 programming_error ("Adding element twice.");
106 elem
->pscore_
= pscore_
;
107 Pointer_group_interface::add_grob (this, ly_symbol2scm ("all-elements"), elem
);
108 scm_gc_unprotect_object (elem
->self_scm ());
113 fixup_refpoints (SCM s
)
115 for (; ly_pair_p (s
); s
= ly_cdr (s
))
117 Grob::fixup_refpoint (ly_car (s
));
124 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
126 Grob
*g
= unsmob_grob (ly_car (s
));
127 if (g
->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
130 Kill no longer needed grobs.
132 Item
* it
= dynamic_cast<Item
*> (g
);
133 if (it
&& Item::is_breakable (it
))
135 it
->find_prebroken_piece (LEFT
)->suicide ();
136 it
->find_prebroken_piece (RIGHT
)->suicide ();
141 g
->do_break_processing ();
145 fixups must be done in broken line_of_scores, because new elements
146 are put over there. */
148 for (int i
=0; i
< broken_intos_
.size (); i
++)
150 Grob
*se
= broken_intos_
[i
];
151 SCM all
= se
->get_property ("all-elements");
152 for (SCM s
= all
; ly_pair_p (s
); s
= ly_cdr (s
))
153 fixup_refpoint (ly_car (s
));
154 count
+= scm_ilength (all
);
158 needed for doing items.
160 fixup_refpoints (get_property ("all-elements"));
162 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
163 unsmob_grob (ly_car (s
))->handle_broken_dependencies ();
164 handle_broken_dependencies ();
166 #if 0 /* don't do this: strange side effects. */
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. uniquify_list ()
172 makes sure that no duplicates are in the list. */
173 for (int i
= 0; i
< line_count
; i
++)
175 SCM all
= broken_intos_
[i
]->get_property ("all-elements");
176 all
= uniquify_list (all
);
180 if (verbose_global_b
)
181 progress_indication (_f ("Element count %d.", count
+ element_count ()));
183 int line_count
= broken_intos_
.size ();
184 SCM lines
= scm_c_make_vector (line_count
, SCM_UNDEFINED
);
186 for (int i
= 0; i
< line_count
; i
++)
188 if (verbose_global_b
)
189 progress_indication ("[");
191 System
*system
= dynamic_cast<System
*> (broken_intos_
[i
]);
192 system
->post_processing ();
193 scm_vector_set_x (lines
, scm_int2num (i
), system
->get_line ());
195 if (verbose_global_b
)
196 progress_indication (to_string (i
) + "]");
205 Find the loose columns in POSNS, and drape them around the columns
206 specified in BETWEEN-COLS. */
208 set_loose_columns (System
* which
, Column_x_positions
const *posns
)
210 for (int i
= 0; i
< posns
->loose_cols_
.size (); i
++)
213 Item
*loose
= dynamic_cast<Item
*> (posns
->loose_cols_
[i
]);
214 Paper_column
* col
= dynamic_cast<Paper_column
*> (loose
);
223 SCM between
= loose
->get_property ("between-cols");
224 if (!ly_pair_p (between
))
228 Item
* l
=dynamic_cast<Item
*> (unsmob_grob (ly_car (between
)));
229 Item
* r
=dynamic_cast<Item
*> (unsmob_grob (ly_cdr (between
)));
236 left
= l
->get_column ();
237 if (!left
->get_system ())
238 left
= left
->find_prebroken_piece (RIGHT
);
242 loose
= right
= r
->get_column ();
246 if (!right
->get_system ())
247 right
= right
->find_prebroken_piece (LEFT
);
250 We divide the remaining space of the column over the left and
251 right side. At the moment, we
254 Grob
* common
= right
->common_refpoint (left
, X_AXIS
);
256 Real rx
= right
->extent (common
, X_AXIS
)[LEFT
];
257 Real lx
= left
->extent (common
, X_AXIS
)[RIGHT
];
258 Real total_dx
= rx
- lx
;
259 Interval cval
=col
->extent (col
, X_AXIS
);
263 We put it in the middle. This is not an ideal solution -- the
264 break alignment code inserts a fixed space before the clef
265 (about 1 SS), while the space following the clef is
266 flexible. In tight situations, the clef will almost be on top
267 of the following note.
270 Real dx
= rx
-lx
- cval
.length ();
271 if (total_dx
< 2* cval
.length ())
274 todo: this is discontinuous. I'm too tired to
275 invent a sliding mechanism. Duh.
284 col
->system_
= which
;
285 col
->translate_axis (- col
->relative_coordinate (common
, X_AXIS
), X_AXIS
);
286 col
->translate_axis (lx
+ dx
- cval
[LEFT
], X_AXIS
);
292 System::break_into_pieces (Array
<Column_x_positions
> const &breaking
)
294 for (int i
=0; i
< breaking
.size (); i
++)
296 System
*system
= dynamic_cast <System
*> (clone ());
299 Link_array
<Grob
> c (breaking
[i
].cols_
);
300 pscore_
->typeset_line (system
);
302 system
->set_bound (LEFT
,c
[0]);
303 system
->set_bound (RIGHT
,c
.top ());
304 for (int j
=0; j
< c
.size (); j
++)
306 c
[j
]->translate_axis (breaking
[i
].config_
[j
],X_AXIS
);
307 dynamic_cast<Paper_column
*> (c
[j
])->system_
= system
;
309 set_loose_columns (system
, &breaking
[i
]);
310 broken_intos_
.push (system
);
315 System::add_column (Paper_column
*p
)
318 SCM cs
= me
->get_property ("columns");
319 Grob
* prev
= ly_pair_p (cs
) ? unsmob_grob (ly_car (cs
)) : 0;
321 p
->rank_
= prev
? Paper_column::get_rank (prev
) + 1 : 0;
323 me
->set_property ("columns", scm_cons (p
->self_scm (), cs
));
325 Axis_group_interface::add_element (me
, p
);
329 System::pre_processing ()
331 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
332 unsmob_grob (ly_car (s
))->discretionary_processing ();
334 if (verbose_global_b
)
335 progress_indication (_f ("Grob count %d", element_count ()));
338 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
339 unsmob_grob (ly_car (s
))->handle_prebroken_dependencies ();
341 fixup_refpoints (get_property ("all-elements"));
343 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
345 Grob
* sc
= unsmob_grob (ly_car (s
));
346 sc
->calculate_dependencies (PRECALCED
, PRECALCING
, ly_symbol2scm ("before-line-breaking-callback"));
349 progress_indication ("\n" + _ ("Calculating line breaks...") + " ");
350 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
352 Grob
* e
= unsmob_grob (ly_car (s
));
353 SCM proc
= e
->get_property ("spacing-procedure");
354 if (ly_procedure_p (proc
))
355 scm_call_1 (proc
, e
->self_scm ());
360 System::post_processing ()
362 for (SCM s
= get_property ("all-elements"); ly_pair_p (s
); s
= ly_cdr (s
))
364 Grob
*g
= unsmob_grob (ly_car (s
));
365 g
->calculate_dependencies (POSTCALCED
, POSTCALCING
,
366 ly_symbol2scm ("after-line-breaking-callback"));
369 Interval
iv (extent (this, Y_AXIS
));
371 programming_error ("System with zero extent.");
373 translate_axis (-iv
[MAX
], Y_AXIS
);
375 /* Generate all stencils to trigger font loads.
376 This might seem inefficient, but Stencils are cached per grob
378 SCM all
= get_property ("all-elements");
379 all
= uniquify_list (all
);
381 this->get_stencil ();
382 for (SCM s
= all
; ly_pair_p (s
); s
= ly_cdr (s
))
384 Grob
*g
= unsmob_grob (ly_car (s
));
392 static int const LAYER_COUNT
= 3;
394 SCM stencils
= SCM_EOL
;
395 if (Stencil
*me
= get_stencil ())
396 stencils
= scm_cons (me
->smobbed_copy (), stencils
);
398 /* Output stencils in three layers: 0, 1, 2. The default layer is
401 Start with layer 3, since scm_cons prepends to list.
404 SCM all
= get_property ("all-elements");
406 for (int i
= LAYER_COUNT
; i
--;)
407 for (SCM s
= all
; ly_pair_p (s
); s
= ly_cdr (s
))
409 Grob
*g
= unsmob_grob (ly_car (s
));
410 Stencil
*stil
= g
->get_stencil ();
412 /* Skip empty stencils and grobs that are not in this layer. */
414 || robust_scm2int (g
->get_property ("layer"), 1) != i
)
417 Offset
o (g
->relative_coordinate (this, X_AXIS
),
418 g
->relative_coordinate (this, Y_AXIS
));
420 Offset extra
= robust_scm2offset (g
->get_property ("extra-offset"),
422 * Staff_symbol_referencer::staff_space (g
);
425 must copy the stencil, for we cannot change the stencil
428 SCM my_stencil
= stil
->smobbed_copy ();
429 unsmob_stencil (my_stencil
)->translate (o
+ extra
);
430 stencils
= scm_cons (my_stencil
, stencils
);
433 if (output_format_global
!= PAGE_LAYOUT
)
435 SCM lastcol
= ly_car (get_property ("columns"));
436 Grob
*g
= unsmob_grob (lastcol
);
438 SCM between
= ly_symbol2scm ("between-system-string");
439 SCM inter
= g
->internal_get_property (between
);
440 if (ly_string_p (inter
))
441 stencils
= scm_cons (scm_cons (between
, inter
), stencils
);
444 Interval
x (extent (this, X_AXIS
));
445 Interval
y (extent (this, Y_AXIS
));
446 Paper_line
*pl
= new Paper_line (Offset (x
.length (), y
.length ()),
449 scm_gc_unprotect_object (pl
->self_scm ());
450 return pl
->self_scm ();
454 System::broken_col_range (Item
const*l
, Item
const*r
) const
456 Link_array
<Item
> ret
;
458 l
= l
->get_column ();
459 r
= r
->get_column ();
460 SCM s
= get_property ("columns");
462 while (ly_pair_p (s
) && ly_car (s
) != r
->self_scm ())
468 while (ly_pair_p (s
) && ly_car (s
) != l
->self_scm ())
470 Paper_column
*c
= dynamic_cast<Paper_column
*> (unsmob_grob (ly_car (s
)));
471 if (Item::is_breakable (c
) && !c
->system_
)
482 Return all columns, but filter out any unused columns , since they might
483 disrupt the spacing problem.
486 System::columns ()const
489 = Pointer_group_interface__extract_grobs (this, (Grob
*) 0, "columns");
491 for (int i
= acs
.size (); i
-- ;)
493 bool brb
= Item::is_breakable (acs
[i
]);
494 bfound
= bfound
|| brb
;
497 the last column should be breakable. Weed out any columns that
498 seem empty. We need to retain breakable columns, in case
499 someone forced a breakpoint.
501 if (!bfound
|| !Paper_column::is_used (acs
[i
]))
507 ADD_INTERFACE (System
,"system-interface",
508 "This is the toplevel object: each object in a score "
509 "ultimately has a System object as its X and Y parent. ",
510 "between-system-string all-elements columns")