Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 /* | 1 /* |
2 This file is part of LilyPond, the GNU music typesetter. | 2 This file is part of LilyPond, the GNU music typesetter. |
3 | 3 |
4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl> | 4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl> |
5 Jan Nieuwenhuizen <janneke@gnu.org> | 5 Jan Nieuwenhuizen <janneke@gnu.org> |
6 | 6 |
7 LilyPond is free software: you can redistribute it and/or modify | 7 LilyPond is free software: you can redistribute it and/or modify |
8 it under the terms of the GNU General Public License as published by | 8 it under the terms of the GNU General Public License as published by |
9 the Free Software Foundation, either version 3 of the License, or | 9 the Free Software Foundation, either version 3 of the License, or |
10 (at your option) any later version. | 10 (at your option) any later version. |
(...skipping 12 matching lines...) Expand all Loading... | |
23 #include <algorithm> | 23 #include <algorithm> |
24 #include <queue> | 24 #include <queue> |
25 #include <set> | 25 #include <set> |
26 using namespace std; | 26 using namespace std; |
27 | 27 |
28 #include "align-interface.hh" | 28 #include "align-interface.hh" |
29 #include "beam.hh" | 29 #include "beam.hh" |
30 #include "direction.hh" | 30 #include "direction.hh" |
31 #include "directional-element-interface.hh" | 31 #include "directional-element-interface.hh" |
32 #include "grob.hh" | 32 #include "grob.hh" |
33 #include "grob-array.hh" | |
34 #include "item.hh" | |
33 #include "international.hh" | 35 #include "international.hh" |
36 #include "least-squares.hh" | |
34 #include "libc-extension.hh" | 37 #include "libc-extension.hh" |
35 #include "main.hh" | 38 #include "main.hh" |
39 #include "note-head.hh" | |
36 #include "output-def.hh" | 40 #include "output-def.hh" |
37 #include "pointer-group-interface.hh" | 41 #include "pointer-group-interface.hh" |
42 #include "spanner.hh" | |
38 #include "staff-symbol-referencer.hh" | 43 #include "staff-symbol-referencer.hh" |
39 #include "stencil.hh" | 44 #include "stencil.hh" |
40 #include "stem.hh" | 45 #include "stem.hh" |
41 #include "warn.hh" | 46 #include "warn.hh" |
42 | 47 |
43 Real | 48 Real |
44 get_detail (SCM alist, SCM sym, Real def) | 49 get_detail (SCM alist, SCM sym, Real def) |
45 { | 50 { |
46 SCM entry = scm_assq (sym, alist); | 51 SCM entry = scm_assq (sym, alist); |
47 | 52 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
120 Real start_score = abs (offset[RIGHT]) + abs (offset[LEFT]); | 125 Real start_score = abs (offset[RIGHT]) + abs (offset[LEFT]); |
121 qs->demerits = start_score / 1000.0; | 126 qs->demerits = start_score / 1000.0; |
122 qs->next_scorer_todo = ORIGINAL_DISTANCE + 1; | 127 qs->next_scorer_todo = ORIGINAL_DISTANCE + 1; |
123 | 128 |
124 return qs; | 129 return qs; |
125 } | 130 } |
126 | 131 |
127 Real | 132 Real |
128 Beam_scoring_problem::y_at (Real x, Beam_configuration const *p) const | 133 Beam_scoring_problem::y_at (Real x, Beam_configuration const *p) const |
129 { | 134 { |
130 return p->y[LEFT] + (x - x_span_[LEFT]) * p->y.delta () / x_span_.delta (); | 135 return p->y[LEFT] + x * p->y.delta () / x_span_; |
131 } | 136 } |
132 | 137 |
133 /****************************************************************/ | 138 /****************************************************************/ |
134 | 139 |
135 /* | 140 /* |
136 TODO: | 141 TODO: |
137 | 142 |
138 - Make all demerits customisable | 143 - Make all demerits customisable |
139 | 144 |
140 - Add demerits for quants per se, as to forbid a specific quant | 145 - Add demerits for quants per se, as to forbid a specific quant |
141 entirely | 146 entirely |
142 */ | 147 */ |
143 | 148 |
144 // This is a temporary hack to see how much we can gain by using a | 149 // This is a temporary hack to see how much we can gain by using a |
145 // priority queue on the beams to score. | 150 // priority queue on the beams to score. |
146 static int score_count = 0; | 151 static int score_count = 0; |
147 LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0, | 152 LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0, |
148 (), | 153 (), |
149 "count number of beam scores.") | 154 "count number of beam scores.") |
150 { | 155 { |
151 return scm_from_int (score_count); | 156 return scm_from_int (score_count); |
152 } | 157 } |
153 | 158 |
154 void Beam_scoring_problem::add_collision (Real x, Interval y, | 159 void Beam_scoring_problem::add_collision (Real x, Interval y, |
155 Real score_factor) | 160 Real score_factor) |
156 { | 161 { |
157 if (edge_dirs_[LEFT] == edge_dirs_[RIGHT]) | 162 // We used to screen for quant range, but no more. |
158 { | |
159 Direction d = edge_dirs_[LEFT]; | |
160 | |
161 Real quant_range_y = quant_range_[LEFT][-d] | |
162 + (x - x_span_[LEFT]) * (quant_range_[RIGHT][-d] - qu ant_range_[LEFT][-d]) / x_span_.delta (); | |
163 | |
164 if (d * (quant_range_y - minmax (d, y[UP], y[DOWN])) > 0) | |
165 { | |
166 return; | |
167 } | |
168 } | |
169 | 163 |
170 Beam_collision c; | 164 Beam_collision c; |
171 c.beam_y_.set_empty (); | 165 c.beam_y_.set_empty (); |
172 | 166 |
173 for (vsize j = 0; j < segments_.size (); j++) | 167 for (vsize j = 0; j < segments_.size (); j++) |
174 { | 168 { |
175 if (segments_[j].horizontal_.contains (x)) | 169 if (segments_[j].horizontal_.contains (x)) |
176 c.beam_y_.add_point (segments_[j].vertical_count_ * beam_translation_); | 170 c.beam_y_.add_point (segments_[j].vertical_count_ * beam_translation_); |
177 if (segments_[j].horizontal_[LEFT] > x) | 171 if (segments_[j].horizontal_[LEFT] > x) |
178 break; | 172 break; |
179 } | 173 } |
180 c.beam_y_.widen (0.5 * beam_thickness_); | 174 c.beam_y_.widen (0.5 * beam_thickness_); |
181 | 175 |
182 c.x_ = x; | 176 c.x_ = x; |
183 | 177 |
184 y *= 1 / staff_space_; | 178 y *= 1 / staff_space_; |
185 c.y_ = y; | 179 c.y_ = y; |
186 c.base_penalty_ = score_factor; | 180 c.base_penalty_ = score_factor; |
187 collisions_.push_back (c); | 181 collisions_.push_back (c); |
188 } | 182 } |
189 | 183 |
190 void Beam_scoring_problem::init_collisions (vector<Grob *> grobs) | 184 void Beam_scoring_problem::init_stems () |
191 { | 185 { |
192 Grob *common_x = NULL; | 186 vector<Spanner *> beams; |
193 segments_ = Beam::get_beam_segments (beam_, &common_x); | 187 if (consistent_broken_slope_) |
194 vector_sort (segments_, beam_segment_less); | 188 { |
195 if (common_[X_AXIS] != common_x) | 189 Spanner *orig = dynamic_cast<Spanner *> (beam_->original ()); |
196 { | 190 if (!orig) |
197 programming_error ("Disagree on common x. Skipping collisions in beam scor ing."); | 191 consistent_broken_slope_ = false; |
198 return; | 192 else if (!orig->broken_intos_.size ()) |
199 } | 193 consistent_broken_slope_ = false; |
200 | 194 else |
201 set<Grob *> stems; | 195 beams.insert (beams.end (), orig->broken_intos_.begin (), orig->broken_i ntos_.end ()); |
202 for (vsize i = 0; i < grobs.size (); i++) | 196 } |
203 { | 197 if (!consistent_broken_slope_) |
204 Box b; | 198 beams.push_back (beam_); |
205 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) | 199 ·· |
206 b[a] = grobs[i]->extent (common_[a], a); | 200 x_span_ = 0.0; |
207 | 201 normal_stem_count_ = 0; |
208 Real width = b[X_AXIS].length (); | 202 for (vsize i = 0; i < beams.size (); i++) |
Keith
2011/10/26 18:57:38
When there is more than one beam in 'beams' what d
MikeSol
2011/10/26 19:18:07
All of Beam_scoring_problem happens in an artifici
| |
209 Real width_factor = sqrt (width / staff_space_); | 203 { |
210 | 204 Interval local_x_span; |
205 extract_grob_set (beams[i], "stems", stems); | |
206 extract_grob_set (beams[i], "covered-grobs", fake_collisions); | |
207 vector<Grob *> collisions; | |
208 ······ | |
209 for (vsize j = 0; j < fake_collisions.size (); j++) | |
210 if (fake_collisions[j]->get_system () == beams[i]->get_system ()) | |
211 collisions.push_back (fake_collisions[j]); | |
212 | |
213 Grob *common[2]; | |
214 for (int a = 2; a--;) | |
215 common[a] = common_refpoint_of_array (stems, beams[i], Axis (a)); | |
216 | |
217 Real x_left = beams[i]->relative_coordinate(common[X_AXIS], X_AXIS); | |
218 ······ | |
219 Drul_array<Grob *> edge_stems (Beam::first_normal_stem (beams[i]), | |
220 Beam::last_normal_stem (beams[i])); | |
211 Direction d = LEFT; | 221 Direction d = LEFT; |
212 do | 222 do |
213 add_collision (b[X_AXIS][d], b[Y_AXIS], width_factor); | 223 local_x_span[d] = edge_stems[d] ? edge_stems[d]->relative_coordinate (co mmon[X_AXIS], X_AXIS) : 0.0; |
214 while (flip (&d) != LEFT); | 224 while (flip (&d) != LEFT); |
215 | 225 |
216 Grob *stem = unsmob_grob (grobs[i]->get_object ("stem")); | 226 Drul_array<bool> dirs_found (0, 0); |
217 if (stem && Stem::has_interface (stem) && Stem::is_normal_stem (stem)) | 227 ······ |
218 { | 228 Real my_y = beams[i]->relative_coordinate (common[Y_AXIS], Y_AXIS); |
219 stems.insert (stem); | 229 |
220 } | 230 Interval beam_width (-1.0,-1.0); |
221 } | 231 for (vsize j = 0; j < stems.size (); j++) |
222 | 232 { |
223 for (set<Grob *>::const_iterator it (stems.begin ()); it != stems.end (); it++ ) | 233 Grob *s = stems[j]; |
224 { | 234 beam_multiplicity_.push_back (Stem::beam_multiplicity (stems[j])); |
225 Grob *s = *it; | 235 head_positions_.push_back (Stem::head_positions (stems[j])); |
226 Real x = s->extent (common_[X_AXIS], X_AXIS).center (); | 236 is_normal_.push_back (Stem::is_normal_stem (stems[j])); |
227 | 237 |
228 Direction stem_dir = get_grob_direction (*it); | 238 Stem_info si (Stem::get_stem_info (s)); |
229 Interval y; | 239 si.scale (1 / staff_space_); |
230 y.set_full (); | 240 stem_infos_.push_back (si); |
231 y[-stem_dir] = Stem::chord_start_y (*it) + (*it)->relative_coordinate (com mon_[Y_AXIS], Y_AXIS) | 241 chord_start_y_.push_back (Stem::chord_start_y (s)); |
232 - beam_->relative_coordinate (common_[Y_AXIS], Y_AXIS); | 242 dirs_found[si.dir_] = true; |
233 | 243 |
234 Real factor = parameters_.STEM_COLLISION_FACTOR; | 244 bool f = to_boolean (s->get_property ("french-beaming")) |
235 if (!unsmob_grob (s->get_object ("beam")) | 245 && s != edge_stems[LEFT] && s != edge_stems[RIGHT]; |
236 && !Stem::flag (s).is_empty ()) | 246 |
237 factor = 1.0; | 247 Real y = Beam::calc_stem_y (beams[i], s, common, local_x_span[LEFT], l ocal_x_span[RIGHT], CENTER, |
238 add_collision (x, y, factor); | 248 Interval (0, 0), f); |
239 } | 249 base_lengths_.push_back (y / staff_space_); |
240 } | 250 stem_xpositions_.push_back (s->relative_coordinate (common[X_AXIS], X_ AXIS) - x_left + x_span_); |
241 | 251 stem_ypositions_.push_back (s->relative_coordinate (common[Y_AXIS], Y_ AXIS) - my_y); |
242 void Beam_scoring_problem::init_stems () | 252 if (is_normal_.back ()) |
243 { | 253 { |
244 extract_grob_set (beam_, "covered-grobs", collisions); | 254 if (beam_width[LEFT] == -1.0) |
245 extract_grob_set (beam_, "stems", stems); | 255 beam_width[LEFT] = stem_xpositions_.back (); |
246 for (int a = 2; a--;) | 256 beam_width[RIGHT] = stem_xpositions_.back (); |
247 { | 257 } |
248 common_[a] = common_refpoint_of_array (stems, beam_, Axis (a)); | 258 } |
249 common_[a] = common_refpoint_of_array (collisions, common_[a], Axis (a)); | 259 |
250 } | 260 edge_dirs_ = Drul_array<Direction> (CENTER, CENTER); |
251 | 261 normal_stem_count_ += Beam::normal_stem_count (beams[i]); |
252 Drul_array<Grob *> edge_stems (Beam::first_normal_stem (beam_), | 262 if (normal_stem_count_) |
253 Beam::last_normal_stem (beam_)); | 263 edge_dirs_ = Drul_array<Direction> (stem_infos_[0].dir_, |
254 Direction d = LEFT; | 264 stem_infos_.back ().dir_); |
255 do | 265 |
256 x_span_[d] = edge_stems[d] ? edge_stems[d]->relative_coordinate (common_[X_A XIS], X_AXIS) : 0.0; | 266 is_xstaff_ = Align_interface::has_interface (common[Y_AXIS]); |
257 while (flip (&d) != LEFT); | 267 is_knee_ = dirs_found[LEFT] && dirs_found[RIGHT]; |
258 | 268 |
259 Drul_array<bool> dirs_found (0, 0); | 269 staff_radius_ = Staff_symbol_referencer::staff_radius (beams[i]); |
260 for (vsize i = 0; i < stems.size (); i++) | 270 edge_beam_counts_ = Drul_array<int> |
261 { | 271 (Stem::beam_multiplicity (stems[0]).length () + 1, |
262 Grob *s = stems[i]; | 272 Stem::beam_multiplicity (stems.back ()).length () + 1) ; |
263 if (!Stem::is_normal_stem (s)) | 273 |
264 continue; | 274 // TODO - why are we dividing by staff_space_? |
265 | 275 beam_translation_ = Beam::get_beam_translation (beams[i]) / staff_space_; |
266 Stem_info si (Stem::get_stem_info (s)); | 276 |
267 si.scale (1 / staff_space_); | 277 d = LEFT; |
268 stem_infos_.push_back (si); | 278 do |
269 dirs_found[si.dir_] = true; | 279 { |
270 | 280 quant_range_[d].set_full (); |
271 bool f = to_boolean (s->get_property ("french-beaming")) | 281 if (!edge_stems[d]) |
272 && s != edge_stems[LEFT] && s != edge_stems[RIGHT]; | 282 continue; |
273 | 283 |
274 Real y = Beam::calc_stem_y (beam_, s, common_, x_span_[LEFT], x_span_[RIGH T], CENTER, | 284 Real stem_offset = edge_stems[d]->relative_coordinate (common[Y_AXIS], Y_AXIS) |
275 Interval (0, 0), f); | 285 - beams[i]->relative_coordinate (common[Y_AXIS], Y_ AXIS); |
276 base_lengths_.push_back (y / staff_space_); | 286 Interval heads = Stem::head_positions (edge_stems[d]) * 0.5 * staff_sp ace_; |
277 stem_xpositions_.push_back (s->relative_coordinate (common_[X_AXIS], X_AXI S)); | 287 |
278 } | 288 Direction ed = edge_dirs_[d]; |
279 | 289 heads.widen (0.5 * staff_space_ |
280 edge_dirs_ = Drul_array<Direction> (CENTER, CENTER); | 290 + (edge_beam_counts_[d] - 1) * beam_translation_ + beam_t hickness_ * .5); |
281 if (stem_infos_.size ()) | 291 quant_range_[d][-ed] = heads[ed] + stem_offset; |
282 { | 292 } |
283 edge_dirs_ = Drul_array<Direction> (stem_infos_[0].dir_, | 293 while (flip (&d) != LEFT); |
284 stem_infos_.back ().dir_); | 294 Grob *common_x = NULL; |
285 } | 295 segments_ = Beam::get_beam_segments (beams[i], &common_x); |
286 | 296 vector_sort (segments_, beam_segment_less); |
287 is_xstaff_ = Align_interface::has_interface (common_[Y_AXIS]); | 297 for (vsize j = 0; j < segments_.size (); j++) |
288 is_knee_ = dirs_found[LEFT] && dirs_found[RIGHT]; | 298 segments_[j].horizontal_ += (x_span_ - x_left); |
289 | 299 |
290 staff_radius_ = Staff_symbol_referencer::staff_radius (beam_); | 300 set<Grob *> colliding_stems; |
291 edge_beam_counts_ = Drul_array<int> | 301 for (vsize j = 0; j < collisions.size (); j++) |
292 (Stem::beam_multiplicity (stems[0]).length () + 1, | 302 { |
293 Stem::beam_multiplicity (stems.back ()).length () + 1); | 303 if (!collisions[j]->is_live ()) |
294 | 304 continue; |
295 // TODO - why are we dividing by staff_space_? | 305 |
296 beam_translation_ = Beam::get_beam_translation (beam_) / staff_space_; | 306 if (Beam::has_interface (collisions[j]) && Beam::is_cross_staff (colli sions[j])) |
297 | 307 continue; |
298 d = LEFT; | 308 |
299 do | 309 Box b; |
300 { | 310 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) |
301 quant_range_[d].set_full (); | 311 b[a] = collisions[j]->extent (common[a], a); |
302 if (!edge_stems[d]) | 312 |
303 continue; | 313 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ()) |
304 | 314 continue; |
305 Real stem_offset = edge_stems[d]->relative_coordinate (common_[Y_AXIS], Y_ AXIS) | 315 |
306 - beam_->relative_coordinate (common_[Y_AXIS], Y_AXIS); | 316 b[X_AXIS] += (x_span_ - x_left); |
307 Interval heads = Stem::head_positions (edge_stems[d]) * 0.5 * staff_space_ ; | 317 Real width = b[X_AXIS].length (); |
308 | 318 Real width_factor = sqrt (width / staff_space_); |
309 Direction ed = edge_dirs_[d]; | 319 |
310 heads.widen (0.5 * staff_space_ | 320 Direction d = LEFT; |
311 + (edge_beam_counts_[d] - 1) * beam_translation_ + beam_thick ness_ * .5); | 321 do |
312 quant_range_[d][-ed] = heads[ed] + stem_offset; | 322 add_collision (b[X_AXIS][d], b[Y_AXIS], width_factor); |
313 } | 323 while (flip (&d) != LEFT); |
314 while (flip (&d) != LEFT); | 324 |
315 | 325 Grob *stem = unsmob_grob (collisions[j]->get_object ("stem")); |
316 init_collisions (collisions); | 326 if (stem && Stem::has_interface (stem) && Stem::is_normal_stem (stem)) |
327 { | |
328 colliding_stems.insert (stem); | |
329 } | |
330 } | |
331 | |
332 for (set<Grob *>::const_iterator it (colliding_stems.begin ()); it != coll iding_stems.end (); it++) | |
333 { | |
334 Grob *s = *it; | |
335 Real x = (s->extent (common[X_AXIS], X_AXIS) - x_left + x_span_).cente r (); | |
336 | |
337 Direction stem_dir = get_grob_direction (*it); | |
338 Interval y; | |
339 y.set_full (); | |
340 y[-stem_dir] = Stem::chord_start_y (*it) + (*it)->relative_coordinate (common[Y_AXIS], Y_AXIS) | |
341 - beams[i]->relative_coordinate (common[Y_AXIS], Y_AXIS ); | |
342 | |
343 Real factor = parameters_.STEM_COLLISION_FACTOR; | |
344 if (!unsmob_grob (s->get_object ("beam"))) | |
345 factor = 1.0; | |
346 add_collision (x, y, factor); | |
347 } | |
348 x_span_ += beams[i]->spanner_length (); | |
349 } | |
350 | |
351 /* | |
352 Here, we eliminate all extremal hangover, be it from non-normal stems | |
353 (like stemlets) or broken beams (if we're not calculating consistent | |
354 slope). | |
355 */ | |
356 if (normal_stem_count_) | |
357 { | |
358 Interval trimmings (0.0, 0.0); | |
359 Direction d = LEFT; | |
360 | |
361 do | |
362 { | |
363 vsize idx = d == LEFT ? first_normal_index () : last_normal_index (); | |
364 trimmings[d] = d * ((d == LEFT ? 0 : x_span_) - stem_xpositions_[idx]) ; | |
365 } | |
366 while (flip (&d) != LEFT); | |
367 | |
368 do | |
369 x_span_ -= trimmings[d]; | |
370 while (flip (&d) != LEFT); | |
371 | |
372 for (vsize i = 0; i < stem_xpositions_.size (); i++) | |
373 stem_xpositions_[i] -= trimmings[LEFT]; | |
374 } | |
317 } | 375 } |
318 | 376 |
319 Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array<Real> ys) | 377 Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array<Real> ys) |
320 { | 378 { |
321 beam_ = me; | 379 beam_ = dynamic_cast<Spanner *> (me); |
322 unquanted_y_ = ys; | 380 unquanted_y_ = ys; |
323 | 381 consistent_broken_slope_ = to_boolean (me->get_property ("consistent-broken-sl ope")); |
324 /* | 382 /* |
325 Calculations are relative to a unit-scaled staff, i.e. the quants are | 383 Calculations are relative to a unit-scaled staff, i.e. the quants are |
326 divided by the current staff_space_. | 384 divided by the current staff_space_. |
327 */ | 385 */ |
328 staff_space_ = Staff_symbol_referencer::staff_space (me); | 386 staff_space_ = Staff_symbol_referencer::staff_space (me); |
329 beam_thickness_ = Beam::get_beam_thickness (me) / staff_space_; | 387 beam_thickness_ = Beam::get_beam_thickness (me) / staff_space_; |
330 line_thickness_ = Staff_symbol_referencer::line_thickness (me) / staff_space_; | 388 line_thickness_ = Staff_symbol_referencer::line_thickness (me) / staff_space_; |
331 | 389 |
332 // This is the least-squares DY, corrected for concave beams. | 390 // This is the least-squares DY, corrected for concave beams. |
333 musical_dy_ = robust_scm2double (me->get_property ("least-squares-dy"), 0); | 391 musical_dy_ = robust_scm2double (me->get_property ("least-squares-dy"), 0); |
334 | 392 |
335 parameters_.fill (me); | 393 parameters_.fill (me); |
336 init_stems (); | 394 init_stems (); |
395 least_squares_positions (); | |
396 slope_damping (); | |
397 shift_region_to_valid (); | |
398 } | |
399 | |
400 // Assuming V is not empty, pick a 'reasonable' point inside V. | |
401 static Real | |
402 point_in_interval (Interval v, Real dist) | |
403 { | |
404 if (isinf (v[DOWN])) | |
405 return v[UP] - dist; | |
406 else if (isinf (v[UP])) | |
407 return v[DOWN] + dist; | |
408 else | |
409 return v.center (); | |
410 } | |
411 | |
412 /* Set stem's shorten property if unset. | |
413 | |
414 TODO: | |
415 take some y-position (chord/beam/nearest?) into account | |
416 scmify forced-fraction | |
417 | |
418 This is done in beam because the shorten has to be uniform over the | |
419 entire beam. | |
420 */ | |
421 | |
422 void | |
423 set_minimum_dy (Grob *me, Real *dy) | |
424 { | |
425 if (*dy) | |
426 { | |
427 /* | |
428 If dy is smaller than the smallest quant, we | |
429 get absurd direction-sign penalties. | |
430 */ | |
431 | |
432 Real ss = Staff_symbol_referencer::staff_space (me); | |
433 Real beam_thickness = Beam::get_beam_thickness (me) / ss; | |
434 Real slt = Staff_symbol_referencer::line_thickness (me) / ss; | |
435 Real sit = (beam_thickness - slt) / 2; | |
436 Real inter = 0.5; | |
437 Real hang = 1.0 - (beam_thickness - slt) / 2; | |
438 | |
439 *dy = sign (*dy) * max (fabs (*dy), | |
440 min (min (sit, inter), hang)); | |
441 } | |
442 } | |
443 | |
444 void | |
445 Beam_scoring_problem::no_visible_stem_positions () | |
446 { | |
447 if (!head_positions_.size ()) | |
448 { | |
449 unquanted_y_ = Interval (0, 0); | |
450 return; | |
451 } | |
452 | |
453 Interval head_positions; | |
454 Slice multiplicity; | |
455 for (vsize i = 0; i < head_positions_.size (); i++) | |
456 { | |
457 head_positions.unite (head_positions_[i]); | |
458 multiplicity.unite (beam_multiplicity_[i]); | |
459 } | |
460 | |
461 Direction dir = get_grob_direction (beam_); | |
462 | |
463 if (!dir) | |
464 programming_error ("The beam should have a direction by now."); | |
465 | |
466 Real y = head_positions.linear_combination (dir) | |
467 * 0.5 * staff_space_ | |
468 + dir * beam_translation_ * (multiplicity.length () + 1); | |
469 | |
470 unquanted_y_ = Interval (y, y); | |
471 } | |
472 | |
473 vsize | |
474 Beam_scoring_problem::first_normal_index () | |
475 { | |
476 for (vsize i = 0; i < is_normal_.size (); i++) | |
477 if (is_normal_[i]) | |
478 return i; | |
479 ·· | |
480 beam_->programming_error ("No normal stems, but asking for first normal stem i ndex."); | |
481 return 0; | |
482 } | |
483 | |
484 vsize | |
485 Beam_scoring_problem::last_normal_index () | |
486 { | |
487 for (vsize i = is_normal_.size (); i--;) | |
488 if (is_normal_[i]) | |
489 return i; | |
490 | |
491 beam_->programming_error ("No normal stems, but asking for first normal stem i ndex."); | |
492 return 0; | |
493 } | |
494 | |
495 void | |
496 Beam_scoring_problem::least_squares_positions () | |
497 { | |
498 if (!normal_stem_count_) | |
499 { | |
500 no_visible_stem_positions (); | |
501 return; | |
502 } | |
503 ·· | |
504 if (stem_infos_.size () < 1) | |
505 return; | |
506 | |
507 vsize fnx = first_normal_index (); | |
508 vsize lnx = last_normal_index (); | |
509 | |
510 Interval ideal (stem_infos_[fnx].ideal_y_ + stem_ypositions_[fnx], | |
511 stem_infos_[lnx].ideal_y_ + stem_ypositions_[lnx]); | |
512 | |
513 Real y = 0; | |
514 Real slope = 0; | |
515 Real dy = 0; | |
516 Real ldy = 0.0; | |
517 if (!ideal.delta ()) | |
518 { | |
519 Interval chord (chord_start_y_[0], | |
520 chord_start_y_.back ()); | |
521 | |
522 /* Simple beams (2 stems) on middle line should be allowed to be | |
523 slightly sloped. | |
524 | |
525 However, if both stems reach middle line, | |
526 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0. | |
527 | |
528 For that case, we apply artificial slope */ | |
529 if (!ideal[LEFT] && chord.delta () && stem_infos_.size () == 2) | |
530 { | |
531 /* FIXME. -> UP */ | |
532 Direction d = (Direction) (sign (chord.delta ()) * UP); | |
533 unquanted_y_[d] = Beam::get_beam_thickness (beam_) / 2; | |
534 unquanted_y_[-d] = -unquanted_y_[d]; | |
535 } | |
536 else | |
537 unquanted_y_ = ideal; | |
538 | |
539 /* | |
540 For broken beams this doesn't work well. In this case, the | |
541 slope esp. of the first part of a broken beam should predict | |
542 where the second part goes. | |
543 */ | |
544 ldy = unquanted_y_[RIGHT] - unquanted_y_[LEFT]; | |
545 } | |
546 else | |
547 { | |
548 vector<Offset> ideals; | |
549 for (vsize i = 0; i < stem_infos_.size (); i++) | |
550 if (is_normal_[i]) | |
551 ideals.push_back (Offset (stem_xpositions_[i], | |
552 stem_infos_[i].ideal_y_ | |
553 + stem_ypositions_[i])); | |
554 | |
555 minimise_least_squares (&slope, &y, ideals); | |
556 | |
557 dy = slope * x_span_; | |
558 | |
559 set_minimum_dy (beam_, &dy); | |
560 | |
561 ldy = dy; | |
562 unquanted_y_ = Interval (y, (y + dy)); | |
563 } | |
564 | |
565 musical_dy_ = ldy; | |
566 } | |
567 | |
568 void | |
569 Beam_scoring_problem::slope_damping () | |
570 { | |
571 if (normal_stem_count_ <= 1) | |
572 return; | |
573 | |
574 SCM s = beam_->get_property ("damping"); | |
575 Real damping = scm_to_double (s); | |
576 Real concaveness = robust_scm2double (beam_->get_property ("concaveness"), 0.0 ); | |
577 if (concaveness >= 10000) | |
578 { | |
579 unquanted_y_[LEFT] = unquanted_y_[RIGHT]; | |
580 musical_dy_ = 0; | |
581 damping = 0; | |
582 } | |
583 | |
584 if (damping) | |
585 { | |
586 Real dy = unquanted_y_[RIGHT] - unquanted_y_[LEFT]; | |
587 | |
588 Real slope = dy && x_span_ ? dy / x_span_ : 0; | |
589 | |
590 slope = 0.6 * tanh (slope) / (damping + concaveness); | |
591 | |
592 Real damped_dy = slope * x_span_; | |
593 | |
594 set_minimum_dy (beam_, &damped_dy); | |
595 | |
596 unquanted_y_[LEFT] += (dy - damped_dy) / 2; | |
597 unquanted_y_[RIGHT] -= (dy - damped_dy) / 2; | |
598 } | |
599 } | |
600 | |
601 void | |
602 Beam_scoring_problem::shift_region_to_valid () | |
603 { | |
604 if (!normal_stem_count_) | |
605 return; | |
606 | |
607 Real beam_dy = unquanted_y_[RIGHT] - unquanted_y_[LEFT]; | |
608 Real slope = x_span_ ? beam_dy / x_span_ : 0.0; | |
609 | |
610 /* | |
611 Shift the positions so that we have a chance of finding good | |
612 quants (i.e. no short stem failures.) | |
613 */ | |
614 Interval feasible_left_point; | |
615 feasible_left_point.set_full (); | |
616 | |
617 for (vsize i = 0; i < stem_infos_.size (); i++) | |
618 { | |
619 // TODO - check for invisible here... | |
620 Real left_y | |
621 = stem_infos_[i].shortest_y_ | |
622 - slope * stem_xpositions_ [i]; | |
623 | |
624 /* | |
625 left_y is now relative to the stem S. We want relative to | |
626 ourselves, so translate: | |
627 */ | |
628 left_y += stem_ypositions_[i]; | |
629 Interval flp; | |
630 flp.set_full (); | |
631 flp[-stem_infos_[i].dir_] = left_y; | |
632 | |
633 feasible_left_point.intersect (flp); | |
634 } | |
635 | |
636 vector<Grob *> filtered; | |
637 /* | |
638 We only update these for objects that are too large for quanting | |
639 to find a workaround. Typically, these are notes with | |
640 stems, and timesig/keysig/clef, which take out the entire area | |
641 inside the staff as feasible. | |
642 | |
643 The code below disregards the thickness and multiplicity of the | |
644 beam. This should not be a problem, as the beam quanting will | |
645 take care of computing the impact those exactly. | |
646 */ | |
647 Real min_y_size = 2.0; | |
648 | |
649 // A list of intervals into which beams may not fall | |
650 vector<Interval> forbidden_intervals; | |
651 | |
652 for (vsize i = 0; i < collisions_.size (); i++) | |
653 { | |
654 if (collisions_[i].x_ < 0 || collisions_[i].x_ > x_span_) | |
655 continue; | |
656 | |
657 if (collisions_[i].y_.length () < min_y_size) | |
658 continue; | |
659 | |
660 Direction d = LEFT; | |
661 do | |
662 { | |
663 Real dy = slope * collisions_[i].x_; | |
664 | |
665 Direction yd = DOWN; | |
666 Interval disallowed; | |
667 do | |
668 { | |
669 Real left_y = collisions_[i].y_[yd] - dy; | |
670 disallowed[yd] = left_y; | |
671 } | |
672 while (flip (&yd) != DOWN); | |
673 | |
674 forbidden_intervals.push_back (disallowed); | |
675 } | |
676 while (flip (&d) != LEFT); | |
677 } | |
678 | |
679 vector_sort (forbidden_intervals, Interval::left_less); | |
680 Real epsilon = 1.0e-10; | |
681 Real beam_left_y = unquanted_y_[LEFT]; | |
682 Interval feasible_beam_placements (beam_left_y, beam_left_y); | |
683 | |
684 /* | |
685 forbidden_intervals contains a vector of intervals in which | |
686 the beam cannot start. it iterates through these intervals, | |
687 pushing feasible_beam_placements epsilon over or epsilon under a | |
688 collision. when this type of change happens, the loop is marked | |
689 as "dirty" and re-iterated. | |
690 | |
691 TODO: figure out a faster ways that this loop can happen via | |
692 a better search algorithm and/or OOP. | |
693 */ | |
694 | |
695 bool dirty = false; | |
696 do | |
697 { | |
698 dirty = false; | |
699 for (vsize i = 0; i < forbidden_intervals.size (); i++) | |
700 { | |
701 Direction d = DOWN; | |
702 do | |
703 { | |
704 if (forbidden_intervals[i][d] == d * infinity_f) | |
705 feasible_beam_placements[d] = d * infinity_f; | |
706 else if (forbidden_intervals[i].contains (feasible_beam_placements [d])) | |
707 { | |
708 feasible_beam_placements[d] = d * epsilon + forbidden_interval s[i][d]; | |
709 dirty = true; | |
710 } | |
711 } | |
712 while (flip (&d) != DOWN); | |
713 } | |
714 } | |
715 while (dirty); | |
716 | |
717 // if the beam placement falls out of the feasible region, we push it | |
718 // to infinity so that it can never be a feasible candidate below | |
719 Direction d = DOWN; | |
720 do | |
721 { | |
722 if (!feasible_left_point.contains (feasible_beam_placements[d])) | |
723 feasible_beam_placements[d] = d * infinity_f; | |
724 } | |
725 while (flip (&d) != DOWN); | |
726 | |
727 if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DO WN] == -infinity_f) && !feasible_left_point.is_empty ()) | |
728 { | |
729 // We are somewhat screwed: we have a collision, but at least | |
730 // there is a way to satisfy stem length constraints. | |
731 beam_left_y = point_in_interval (feasible_left_point, 2.0); | |
732 } | |
733 else if (!feasible_left_point.is_empty ()) | |
734 { | |
735 // Only one of them offers is feasible solution. Pick that one. | |
736 if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP])) | |
737 beam_left_y = feasible_beam_placements[UP]; | |
738 else | |
739 beam_left_y = feasible_beam_placements[DOWN]; | |
740 } | |
741 else | |
742 { | |
743 // We are completely screwed. | |
744 beam_->warning (_ ("no viable initial configuration found: may not find go od beam slope")); | |
745 } | |
746 | |
747 unquanted_y_ = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy)); | |
337 } | 748 } |
338 | 749 |
339 void | 750 void |
340 Beam_scoring_problem::generate_quants (vector<Beam_configuration *> *scores) con st | 751 Beam_scoring_problem::generate_quants (vector<Beam_configuration *> *scores) con st |
341 { | 752 { |
342 int region_size = (int) parameters_.REGION_SIZE; | 753 int region_size = (int) parameters_.REGION_SIZE; |
343 | 754 |
344 // Knees and collisions are harder, lets try some more possibilities | 755 // Knees and collisions are harder, lets try some more possibilities |
345 if (is_knee_) | 756 if (is_knee_) |
346 region_size += 2; | 757 region_size += 2; |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
453 { | 864 { |
454 vector<Beam_configuration *> configs; | 865 vector<Beam_configuration *> configs; |
455 generate_quants (&configs); | 866 generate_quants (&configs); |
456 | 867 |
457 if (configs.empty ()) | 868 if (configs.empty ()) |
458 { | 869 { |
459 programming_error ("No viable beam quanting found. Using unquanted y valu e."); | 870 programming_error ("No viable beam quanting found. Using unquanted y valu e."); |
460 return unquanted_y_; | 871 return unquanted_y_; |
461 } | 872 } |
462 | 873 |
874 if (to_boolean (beam_->get_property ("skip-quanting"))) | |
875 return unquanted_y_; | |
876 | |
463 Beam_configuration *best = NULL; | 877 Beam_configuration *best = NULL; |
464 | 878 |
465 bool debug | 879 bool debug |
466 = to_boolean (beam_->layout ()->lookup_variable (ly_symbol2scm ("debug-beam- scoring"))); | 880 = to_boolean (beam_->layout ()->lookup_variable (ly_symbol2scm ("debug-beam- scoring"))); |
467 SCM inspect_quants = beam_->get_property ("inspect-quants"); | 881 SCM inspect_quants = beam_->get_property ("inspect-quants"); |
468 if (scm_is_pair (inspect_quants)) | 882 if (scm_is_pair (inspect_quants)) |
469 { | 883 { |
470 debug = true; | 884 debug = true; |
471 best = force_score (inspect_quants, configs); | 885 best = force_score (inspect_quants, configs); |
472 } | 886 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
517 if (configs[i]->done ()) | 931 if (configs[i]->done ()) |
518 completed++; | 932 completed++; |
519 } | 933 } |
520 | 934 |
521 string card = best->score_card_ + to_string (" c%d/%d", completed, configs .size ()); | 935 string card = best->score_card_ + to_string (" c%d/%d", completed, configs .size ()); |
522 beam_->set_property ("annotation", ly_string2scm (card)); | 936 beam_->set_property ("annotation", ly_string2scm (card)); |
523 } | 937 } |
524 #endif | 938 #endif |
525 | 939 |
526 junk_pointers (configs); | 940 junk_pointers (configs); |
941 if (consistent_broken_slope_) | |
942 { | |
943 Interval normalized_endpoints = robust_scm2interval (beam_->get_property ( "normalized-endpoints"), Interval (0, 1)); | |
944 Real y_length = final_positions[RIGHT] - final_positions[LEFT]; | |
945 | |
946 final_positions[LEFT] += normalized_endpoints[LEFT] * y_length; | |
947 final_positions[RIGHT] -= (1 - normalized_endpoints[RIGHT]) * y_length; | |
Keith
2011/10/26 18:57:38
At what horizontal locations are these final_posit
MikeSol
2011/10/26 19:18:07
Including overhang. This is improved in the most
| |
948 } | |
949 | |
527 return final_positions; | 950 return final_positions; |
528 } | 951 } |
529 | 952 |
530 void | 953 void |
531 Beam_scoring_problem::score_stem_lengths (Beam_configuration *config) const | 954 Beam_scoring_problem::score_stem_lengths (Beam_configuration *config) const |
532 { | 955 { |
533 Real limit_penalty = parameters_.STEM_LENGTH_LIMIT_PENALTY; | 956 Real limit_penalty = parameters_.STEM_LENGTH_LIMIT_PENALTY; |
534 Drul_array<Real> score (0, 0); | 957 Drul_array<Real> score (0, 0); |
535 Drul_array<int> count (0, 0); | 958 Drul_array<int> count (0, 0); |
536 | 959 |
537 for (vsize i = 0; i < stem_xpositions_.size (); i++) | 960 for (vsize i = 0; i < stem_xpositions_.size (); i++) |
538 { | 961 { |
962 if (!is_normal_[i]) | |
963 continue; | |
964 | |
539 Real x = stem_xpositions_[i]; | 965 Real x = stem_xpositions_[i]; |
540 Real dx = x_span_.delta (); | 966 Real dx = x_span_; |
541 Real beam_y = dx | 967 Real beam_y = dx |
542 ? config->y[RIGHT] * (x - x_span_[LEFT]) / dx + config->y[LE FT] * (x_span_[RIGHT] - x) / dx | 968 ? config->y[RIGHT] * x / dx + config->y[LEFT] * (x_span_ - x ) / dx |
543 : (config->y[RIGHT] + config->y[LEFT]) / 2; | 969 : (config->y[RIGHT] + config->y[LEFT]) / 2; |
544 Real current_y = beam_y + base_lengths_[i]; | 970 Real current_y = beam_y + base_lengths_[i]; |
545 Real length_pen = parameters_.STEM_LENGTH_DEMERIT_FACTOR; | 971 Real length_pen = parameters_.STEM_LENGTH_DEMERIT_FACTOR; |
546 | 972 |
547 Stem_info info = stem_infos_[i]; | 973 Stem_info info = stem_infos_[i]; |
548 Direction d = info.dir_; | 974 Direction d = info.dir_; |
549 | 975 |
550 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)) ); | 976 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)) ); |
551 | 977 |
552 Real ideal_diff = d * (current_y - info.ideal_y_); | 978 Real ideal_diff = d * (current_y - info.ideal_y_); |
(...skipping 28 matching lines...) Expand all Loading... | |
581 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for | 1007 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for |
582 complex beaming patterns, horizontal is often a good choice. | 1008 complex beaming patterns, horizontal is often a good choice. |
583 | 1009 |
584 TODO: find a way to incorporate the complexity of the beam in this | 1010 TODO: find a way to incorporate the complexity of the beam in this |
585 penalty. | 1011 penalty. |
586 */ | 1012 */ |
587 if (sign (damped_dy) != sign (dy)) | 1013 if (sign (damped_dy) != sign (dy)) |
588 { | 1014 { |
589 if (!dy) | 1015 if (!dy) |
590 { | 1016 { |
591 if (fabs (damped_dy / x_span_.delta ()) > parameters_.ROUND_TO_ZERO_SL OPE) | 1017 if (fabs (damped_dy / x_span_) > parameters_.ROUND_TO_ZERO_SLOPE) |
592 dem += parameters_.DAMPING_DIRECTION_PENALTY; | 1018 dem += parameters_.DAMPING_DIRECTION_PENALTY; |
593 else | 1019 else |
594 dem += parameters_.HINT_DIRECTION_PENALTY; | 1020 dem += parameters_.HINT_DIRECTION_PENALTY; |
595 } | 1021 } |
596 else | 1022 else |
597 dem += parameters_.DAMPING_DIRECTION_PENALTY; | 1023 dem += parameters_.DAMPING_DIRECTION_PENALTY; |
598 } | 1024 } |
599 | 1025 |
600 config->add (dem, "Sd"); | 1026 config->add (dem, "Sd"); |
601 } | 1027 } |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
772 Real scale_free | 1198 Real scale_free |
773 = max (parameters_.COLLISION_PADDING - dist, 0.0) / | 1199 = max (parameters_.COLLISION_PADDING - dist, 0.0) / |
774 parameters_.COLLISION_PADDING; | 1200 parameters_.COLLISION_PADDING; |
775 demerits | 1201 demerits |
776 += collisions_[i].base_penalty_ * | 1202 += collisions_[i].base_penalty_ * |
777 pow (scale_free, 3) * parameters_.COLLISION_PENALTY; | 1203 pow (scale_free, 3) * parameters_.COLLISION_PENALTY; |
778 } | 1204 } |
779 | 1205 |
780 config->add (demerits, "C"); | 1206 config->add (demerits, "C"); |
781 } | 1207 } |
LEFT | RIGHT |