~ubuntu-branches/ubuntu/precise/lilypond/precise

« back to all changes in this revision

Viewing changes to lily/note-head.cc

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Bushnell, BSG
  • Date: 2006-12-19 10:18:12 UTC
  • mfrom: (3.1.4 feisty)
  • Revision ID: james.westby@ubuntu.com-20061219101812-7awtjkp0i393wxty
Tags: 2.8.7-3
scripts/midi2ly.py: When setting DATADIR, find Lilypond python files
in the @TOPLEVEL_VERSION@ directory, not 'current'.  Patch thanks to
Chris Lamb (chris@chris-lamb.co.uk).  (Closes: #400550)

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 
4
4
  source file of the GNU LilyPond music typesetter
5
5
 
6
 
  (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
 
6
  (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7
7
*/
8
 
#include <math.h>
9
 
#include <ctype.h>
10
 
 
11
 
#include "staff-symbol.hh"
12
 
#include "misc.hh"
 
8
 
 
9
#include "note-head.hh"
 
10
 
 
11
#include <cmath>
 
12
#include <cctype>
 
13
#include <algorithm>            //  min, max
 
14
using namespace std;
 
15
 
 
16
#include "directional-element-interface.hh"
13
17
#include "dots.hh"
14
 
#include "note-head.hh"
15
 
#include "warn.hh"
16
18
#include "font-interface.hh"
17
 
#include "stencil.hh"
18
 
#include "event.hh"
 
19
#include "international.hh"
 
20
#include "lookup.hh"
 
21
#include "misc.hh"
 
22
#include "music.hh"
 
23
#include "output-def.hh"
19
24
#include "rhythmic-head.hh"
20
25
#include "staff-symbol-referencer.hh"
21
 
#include "lookup.hh"
22
 
#include "paper-def.hh"
 
26
#include "staff-symbol.hh"
 
27
#include "warn.hh"
23
28
 
24
29
/*
25
 
  Note_head contains the code for printing note heads.
26
 
 
27
 
  Ledger lines:
28
 
 
29
 
  It also contains the ledger lines, for historical reasons.  Ledger
30
 
  lines are somewhat of a PITA. In some cases, they take up no space, in
31
 
  some cases they don't:
32
 
 
33
 
  DO take space:
34
 
 
35
 
  - when ledgered notes are juxtaposed: there should be some white
36
 
   space between the ledger lines.
37
 
 
38
 
  - when accidentals are near: the accidentals should not be on the
39
 
  ledger lines
40
 
 
41
 
  [both tips by Heinz Stolba from Universal Edition].
42
 
 
43
 
  DO NOT take space into account:
44
 
 
45
 
  - for basically everything else, e.g. swapping ledgered notes on
46
 
   clustered chords, spacing between ledgered and unledgered notes.
47
 
  
48
 
  TODO: fix this. It is not feasible to have a special grob for
49
 
  ledgers, since you basically don't know if there will be ledgers,
50
 
  unless you know at interpretation phase already 1. the Y-position,
51
 
  2. the number of staff lines. It's not yet specced when both pieces
52
 
  of information are there, so for now, it is probably better to build
53
 
  special support for ledgers into the accidental and separation-item
54
 
  code.
55
 
 
56
 
  (Besides a separate ledger seems overkill. For what else would
57
 
  it be useful?)
58
 
 
 
30
  clean up the mess left by ledger line handling.
59
31
*/
60
 
 
61
 
/*
62
 
  TODO: ledger lines are also a property of the staff. Maybe move them
63
 
  to there?
64
 
 */
65
 
Stencil
66
 
Note_head::brew_ledger_lines (Grob *me,
67
 
                              int pos,
68
 
                              int interspaces,
69
 
                              Interval x_extent,
70
 
                              Real left_shorten,
71
 
                              bool take_space)
72
 
{
73
 
  Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
74
 
  Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
75
 
  int line_count = (abs (pos) < interspaces)
76
 
    ? 0
77
 
    : (abs (pos) - interspaces) / 2;
78
 
  Stencil stencil = Stencil ();
79
 
 
80
 
 
81
 
  if (line_count)
82
 
    {
83
 
      Real ledgerlinethickness =
84
 
        Staff_symbol::get_ledger_line_thickness (staff);
85
 
      Real blotdiameter = ledgerlinethickness;
86
 
      Interval y_extent =
87
 
        Interval (-0.5*(ledgerlinethickness),
88
 
                  +0.5*(ledgerlinethickness));
89
 
      Stencil proto_ledger_line =
90
 
        Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
91
 
 
92
 
      x_extent[LEFT] += left_shorten;
93
 
      Stencil proto_first_line =
94
 
        Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
95
 
 
96
 
      if (!take_space)
97
 
        {
98
 
          proto_ledger_line.set_empty (true);
99
 
          proto_first_line.set_empty (true);
100
 
        }
101
 
      
102
 
      Direction dir = (Direction)sign (pos);
103
 
      Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
104
 
        ? 0.0
105
 
        : -dir * inter_f;
106
 
      
107
 
      for (int i = 0; i < line_count; i++)
108
 
        {
109
 
          Stencil ledger_line ((i == 0) 
110
 
                                ? proto_first_line
111
 
                                : proto_ledger_line
112
 
                                );
113
 
          ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
114
 
          stencil.add_stencil (ledger_line);
115
 
        }
116
 
    }
117
 
 
118
 
  return stencil;
119
 
}
120
 
 
121
 
Stencil
122
 
internal_print (Grob *me, bool with_ledgers)
123
 
{
124
 
  SCM style  = me->get_property ("style");
125
 
  if (!gh_symbol_p (style))
126
 
    {
127
 
      return Stencil ();
128
 
    }
129
 
 
130
 
  SCM log = gh_int2scm (Note_head::get_balltype (me));
131
 
  SCM proc = me->get_property ("glyph-name-procedure");
132
 
  SCM scm_font_char = scm_call_2 (proc, log, style);
133
 
  String font_char = "noteheads-" + ly_scm2string (scm_font_char);
134
 
 
135
 
  Font_metric * fm = Font_interface::get_default_font (me);
136
 
  Stencil out = fm->find_by_name (font_char);
137
 
  if (out.is_empty ())
138
 
    {
139
 
      me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
140
 
    }
141
 
 
142
 
  int interspaces = Staff_symbol_referencer::line_count (me)-1;
143
 
  int pos = Staff_symbol_referencer::get_rounded_position (me);
144
 
  if (with_ledgers && interspaces >= 0
145
 
      && abs (pos) - interspaces > 1)
146
 
    {
147
 
      Interval ledger_size = out.extent (X_AXIS);
148
 
      ledger_size.widen ( ledger_size.length ()/4);
149
 
 
150
 
      Real left_shorten =0.0;
151
 
      if (Grob * g = unsmob_grob (me->get_property ("accidental-grob")))
152
 
        {
153
 
          /*
154
 
            make a little room for accidentals.
155
 
          
156
 
            TODO: this will look silly if a chord has ledger lines,
157
 
            and only the bottom note has an accidental.
158
 
          */
159
 
 
160
 
          Grob *common = g->common_refpoint (me, X_AXIS);
161
 
          Real d =
162
 
            linear_combination (Drul_array<Real> (me->extent (common, X_AXIS)[LEFT],
163
 
                                                  g->extent (common, X_AXIS)[RIGHT]),
164
 
                                
165
 
                                0.5);
166
 
 
167
 
          left_shorten =  (-ledger_size[LEFT] + d) >?  0 ;
168
 
 
169
 
          /*
170
 
            TODO: shorten 2 ledger lines for the case natural +
171
 
            downstem.
172
 
           */
173
 
        }
174
 
 
175
 
      out.add_stencil (Note_head::brew_ledger_lines (me, pos, interspaces,
176
 
                                                      ledger_size,
177
 
                                                      left_shorten,
178
 
                                                      false));
179
 
    }
 
32
static Stencil
 
33
internal_print (Grob *me, string *font_char)
 
34
{
 
35
  SCM style = me->get_property ("style");
 
36
  if (!scm_is_symbol (style))
 
37
    style = ly_symbol2scm ("default");
 
38
 
 
39
  string suffix = to_string (min (robust_scm2int (me->get_property ("duration-log"), 2), 2));
 
40
  if (style != ly_symbol2scm ("default"))
 
41
    {
 
42
      SCM gn = me->get_property ("glyph-name");
 
43
      if (scm_is_string (gn))
 
44
        suffix = ly_scm2string (gn);
 
45
    }
 
46
 
 
47
  Font_metric *fm = Font_interface::get_default_font (me);
 
48
 
 
49
  string idx = "noteheads.s" + suffix;
 
50
 
 
51
  Stencil out = fm->find_by_name (idx);
 
52
  if (out.is_empty ())
 
53
    {
 
54
      string prefix = "noteheads.";
 
55
      Grob *stem = unsmob_grob (me->get_object ("stem"));
 
56
      Direction stem_dir = stem ? get_grob_direction (stem) : CENTER;
 
57
 
 
58
      if (stem_dir == CENTER)
 
59
        programming_error ("must have stem dir for note head");
 
60
 
 
61
      idx = prefix + ((stem_dir == UP) ? "u" : "d") + suffix;
 
62
      out = fm->find_by_name (idx);
 
63
    }
 
64
 
 
65
  if (out.is_empty ())
 
66
    {
 
67
      me->warning (_f ("note head `%s' not found", idx.c_str ()));
 
68
      out = Stencil (Box (Interval (0, 0), Interval (0, 0)), SCM_EOL);
 
69
    }
 
70
  else
 
71
    *font_char = idx;
 
72
 
180
73
  return out;
181
74
}
182
75
 
183
 
 
184
 
MAKE_SCHEME_CALLBACK (Note_head,print,1);
185
 
SCM
186
 
Note_head::print (SCM smob)  
187
 
{
188
 
  Grob *me = unsmob_grob (smob);
189
 
 
190
 
  /*
191
 
    ledgers don't take space. See top of file.
192
 
   */
193
 
  return internal_print (me, true).smobbed_copy ();
194
 
}
195
 
 
196
76
/*
197
 
  Compute the width the head without ledgers.
198
 
 
199
 
  -- there used to be some code from the time that ledgers
200
 
  did take space. Nowadays, we can simply take the standard extent.
 
77
  TODO: make stem X-parent of notehead. 
201
78
 */
202
 
Interval
203
 
Note_head::head_extent (Grob *me, Axis a)
204
 
{
205
 
  SCM brewer = me->get_property ("print-function");
206
 
  if (brewer == Note_head::print_proc)
207
 
    {
208
 
      Stencil mol = internal_print (me, false);
209
 
  
210
 
      if (!mol.is_empty ())
211
 
        return mol.extent (a);
212
 
    }
213
 
  else
214
 
    {
215
 
      Stencil * mol = me->get_stencil ();
216
 
      if (mol)
217
 
        return  mol->extent (a) ;
218
 
    }
219
 
  
220
 
  return Interval (0,0);
221
 
}
222
 
 
223
 
/*
224
 
  This is necessary to prevent a cyclic dependency: the appearance of
225
 
  the ledgers depends on positioning, so the Grob::get_stencil () can
226
 
  not be used for determining the note head extent.
227
 
  
228
 
 */ 
229
 
MAKE_SCHEME_CALLBACK (Note_head,extent,2);
230
 
SCM
231
 
Note_head::extent (SCM smob, SCM axis)  
232
 
{
233
 
  Grob *me = unsmob_grob (smob);
234
 
 
235
 
  return ly_interval2scm (head_extent (me, (Axis) gh_scm2int (axis)));
236
 
}
237
 
 
238
 
MAKE_SCHEME_CALLBACK (Note_head,brew_ez_stencil,1);
239
 
SCM
240
 
Note_head::brew_ez_stencil (SCM smob)
241
 
{
242
 
  Grob *me = unsmob_grob (smob);
243
 
  int l = Note_head::get_balltype (me);
244
 
 
245
 
  int b = (l >= 2);
246
 
 
247
 
  SCM cause = me->get_property ("cause");
248
 
  SCM spitch = unsmob_music (cause)->get_property ("pitch");
249
 
  Pitch* pit =  unsmob_pitch (spitch);
250
 
 
251
 
  SCM idx = gh_int2scm (pit->get_notename ());
252
 
  SCM names = me->get_property ("note-names");
253
 
  SCM charstr = SCM_EOL;
254
 
  if (gh_vector_p (names))
255
 
    charstr = scm_vector_ref (names, idx);
256
 
  else
257
 
    {
258
 
      char s[2] = "a";
259
 
      s[0] = (pit->get_notename () + 2)%7 + 'a';
260
 
      s[0] = toupper (s[0]);
261
 
      charstr = scm_makfrom0str (s);
262
 
    }
263
 
  
264
 
  SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
265
 
                       charstr,
266
 
                       gh_int2scm (b),
267
 
                       gh_int2scm (1-b),
268
 
                       SCM_UNDEFINED);
269
 
  Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
270
 
  Stencil m (bx, at);
271
 
 
272
 
  int pos = Staff_symbol_referencer::get_rounded_position (me);
273
 
  int interspaces = Staff_symbol_referencer::line_count (me)-1;
274
 
  if (abs (pos) - interspaces > 1)
275
 
    {
276
 
      Interval hd = m.extent (X_AXIS);
277
 
      hd.widen ( hd.length ()/4);
278
 
      m.add_stencil (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
279
 
    }
280
 
 
281
 
  return m.smobbed_copy ();
282
 
}
283
 
 
 
79
MAKE_SCHEME_CALLBACK (Note_head, stem_x_shift, 1);
 
80
SCM
 
81
Note_head::stem_x_shift (SCM smob)
 
82
{
 
83
  Grob *me = unsmob_grob (smob);
 
84
  Grob *stem = unsmob_grob (me->get_object ("stem"));
 
85
  if (stem)
 
86
    (void) stem->get_property ("positioning-done");
 
87
 
 
88
  return scm_from_int (0);
 
89
}
 
90
 
 
91
MAKE_SCHEME_CALLBACK (Note_head, print, 1);
 
92
SCM
 
93
Note_head::print (SCM smob)
 
94
{
 
95
  Grob *me = unsmob_grob (smob);
 
96
 
 
97
  string idx;
 
98
  return internal_print (me, &idx).smobbed_copy ();
 
99
}
284
100
 
285
101
Real
286
102
Note_head::stem_attachment_coordinate (Grob *me, Axis a)
287
103
{
288
 
  SCM brewer = me->get_property ("print-function");
289
 
  Font_metric * fm  = Font_interface::get_default_font (me);
290
 
  
291
 
  if (brewer == Note_head::print_proc)
 
104
  Offset off = robust_scm2offset (me->get_property ("stem-attachment"),
 
105
                                  Offset (0,0));
 
106
  
 
107
  return off [a];
 
108
}
 
109
 
 
110
MAKE_SCHEME_CALLBACK(Note_head, calc_stem_attachment, 1);
 
111
SCM
 
112
Note_head::calc_stem_attachment (SCM smob)
 
113
{
 
114
  Grob *me  = unsmob_grob (smob);
 
115
  Font_metric *fm = Font_interface::get_default_font (me);
 
116
  string key;
 
117
  internal_print (me, &key);
 
118
 
 
119
  Offset att;
 
120
  
 
121
  int k = fm->name_to_index (key);
 
122
  if (k >= 0)
292
123
    {
293
 
      SCM style  = me->get_property ("style");
294
 
      if (!gh_symbol_p (style))
295
 
        {
296
 
          return 0.0;
297
 
        }
298
 
      
299
 
      SCM log = gh_int2scm (Note_head::get_balltype (me));
300
 
      SCM proc = me->get_property ("glyph-name-procedure");
301
 
      SCM scm_font_char = scm_call_2 (proc, log, style);
302
 
      String font_char = "noteheads-" + ly_scm2string (scm_font_char);
303
 
 
304
 
      int k = fm->name_to_index (font_char) ;
305
 
 
306
 
      if (k >= 0)
307
 
        {
308
 
          Box b = fm->get_indexed_char (k);
309
 
          Offset wxwy = fm->get_indexed_wxwy (k);
 
124
      Box b = fm->get_indexed_char (k);
 
125
      Offset wxwy = fm->attachment_point (key);
 
126
      for (int i = X_AXIS ; i < NO_AXES; i++)
 
127
        {
 
128
          Axis a = Axis (i);
 
129
          
310
130
          Interval v = b[a];
311
131
          if (!v.is_empty ())
312
 
            return 2 * (wxwy[a] - v.center ()) / v.length ();
 
132
            {
 
133
              att[a] = (2 * (wxwy[a] - v.center ()) / v.length ());
 
134
            }
313
135
        }
314
136
    }
315
 
  
316
 
  /*
317
 
    Fallback
318
 
   */
319
 
  SCM v = me->get_property ("stem-attachment-function");
320
 
  if (!gh_procedure_p (v))
321
 
    return 0.0;
322
 
  
323
 
  SCM result = scm_call_2 (v, me->self_scm (), gh_int2scm (a));
324
 
  if (!gh_pair_p (result))
325
 
    return 0.0;
326
137
 
327
 
  result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
328
 
  
329
 
  return robust_scm2double (result,0);
 
138
  return ly_offset2scm (att);
330
139
}
331
140
 
 
141
 
332
142
int
333
 
Note_head::get_balltype (Grob*me) 
 
143
Note_head::get_balltype (Grob *me)
334
144
{
335
145
  SCM s = me->get_property ("duration-log");
336
 
  return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
 
146
  return scm_is_number (s) ? min (int (scm_to_int (s)), 2) : 0;
337
147
}
338
148
 
339
 
ADD_INTERFACE (Note_head,"note-head-interface",
340
 
  "Note head",
341
 
  "note-names glyph-name-procedure accidental-grob style stem-attachment-function");
 
149
ADD_INTERFACE (Note_head, "note-head-interface",
 
150
               "Note head",
 
151
 
 
152
               /* properties */
 
153
               "note-names "
 
154
               "accidental-grob "
 
155
               "glyph-name "
 
156
               "stem-attachment "
 
157
               "style "
 
158
               );
342
159