*** empty log message ***
[lilypond.git] / lily / porrectus.cc
blobe544cfb80c857e31a73062f2c0787bcdc5156f28
1 /*
2 porrectus.cc -- implement Porrectus
4 Copyright (c) 2001--2003 Juergen Reuter
6 written for the GNU LilyPond music typesetter
8 TODO: --> see porrectus-engraver.cc
9 */
11 #include "staff-symbol-referencer.hh"
12 #include "porrectus.hh"
13 #include "item.hh"
14 #include "molecule.hh"
15 #include "pitch.hh"
16 #include "lookup.hh"
17 #include "warn.hh"
18 #include "dimensions.hh"
19 #include "direction.hh"
20 #include "bezier.hh"
21 #include "font-interface.hh"
22 #include "paper-def.hh"
23 #include "note-head.hh"
24 #include "math.h" // rint
26 void
27 Porrectus::set_left_head (Grob *me, Item *left_head)
29 if (left_head != 0)
31 me->set_grob_property ("left-head", left_head->self_scm());
33 else
35 programming_error (_ ("(left_head == 0)"));
36 me->set_grob_property ("left-head", SCM_EOL);
40 Item *
41 Porrectus::get_left_head (Grob *me)
43 SCM left_head_scm = me->get_grob_property ("left-head");
44 if (left_head_scm == SCM_EOL)
46 programming_error (_ ("undefined left_head"));
47 return 0;
49 else
51 Item *left_head = unsmob_item (left_head_scm);
52 return left_head;
56 void
57 Porrectus::set_right_head (Grob *me, Item *right_head)
59 if (right_head != 0)
61 me->set_grob_property ("right-head", right_head->self_scm());
63 else
65 programming_error (_ ("(right_head == 0)"));
66 me->set_grob_property ("right-head", SCM_EOL);
70 Item *
71 Porrectus::get_right_head (Grob *me)
73 SCM right_head_scm = me->get_grob_property ("right-head");
74 if (right_head_scm == SCM_EOL)
76 programming_error (_ ("undefined right_head"));
77 return 0;
79 else
81 Item *right_head = unsmob_item (right_head_scm);
82 return right_head;
86 MAKE_SCHEME_CALLBACK (Porrectus,brew_molecule,1);
87 SCM
88 Porrectus::brew_molecule (SCM smob)
90 Item *me = (Item *)unsmob_grob (smob);
92 Item *left_head = get_left_head (me);
93 Item *right_head = get_right_head (me);
94 if (!left_head || !right_head)
96 me->warning (_ ("junking lonely porrectus"));
97 me->suicide ();
98 return SCM_EOL;
101 SCM scm_style = me->get_grob_property ("style");
102 String style;
103 if ((gh_symbol_p (scm_style)) && (scm_style != SCM_EOL))
104 style = ly_symbol2string (scm_style);
105 else {
106 me->warning (_ ("porrectus style undefined; using mensural"));
107 style = "mensural";
110 bool solid = to_boolean (me->get_grob_property ("solid"));
111 bool add_stem = to_boolean (me->get_grob_property ("add-stem"));
114 * This property is called stem-direction (rather than direction)
115 * since it only refers to this grob's stem (or, more precisely, its
116 * "cauda"), but not the grob as a whole.
118 SCM stem_direction_scm = me->get_grob_property ("direction");
119 Direction stem_direction =
120 gh_number_p (stem_direction_scm) ? to_dir (stem_direction_scm) : DOWN;
121 if (!stem_direction)
122 stem_direction = DOWN;
125 TODO: revise name.
127 bool auto_properties = to_boolean (me->get_grob_property ("auto-properties"));
128 if (auto_properties)
129 // determine add_stem and stem_direction automatically from durations
131 if (String::compare (style, "mensural") != 0)
132 me->warning (String("auto-property should be used for\r\n") +
133 String("mensural style porrectus only; trying anyway"));
135 int left_duration =
136 gh_scm2int (left_head->get_grob_property ("duration-log"));
137 int right_duration =
138 gh_scm2int (right_head->get_grob_property ("duration-log"));
140 if ((left_duration == -1) && (right_duration == -1))
142 // brevis -- brevis:
143 // cum proprietate et sine perfectione (c.s.)
144 add_stem = true;
145 stem_direction = DOWN;
147 else if ((left_duration == -2) && (right_duration == -1))
149 // longa -- brevis:
150 // sine proprietate et sine perfectione (s.s.)
151 add_stem = false;
153 else if ((left_duration == 0) && (right_duration == 0))
155 // semibrevis -- semibrevis:
156 // cum opposita proprietate (c.o.p.)
157 add_stem = true;
158 stem_direction = UP;
160 else
162 me->warning (String("auto-property: failed determining porrectus\r\n") +
163 String("properties due to improper durations; ") +
164 String("using user-supplied properties"));
168 Real left_position_f = Staff_symbol_referencer::get_position (left_head);
169 Real right_position_f = Staff_symbol_referencer::get_position (right_head);
170 Real interval = right_position_f - left_position_f;
172 Molecule molecule;
174 SCM line_thickness_scm = me->get_grob_property ("thickness");
175 Real line_thickness;
176 if (gh_number_p (line_thickness_scm))
178 line_thickness = gh_scm2double (line_thickness_scm);
180 else
182 line_thickness = 1.0;
184 Real thickness =
185 line_thickness * me->get_paper ()->get_var ("linethickness");
187 SCM porrectus_width_scm = me->get_grob_property ("width");
188 Real porrectus_width;
189 if (gh_number_p (porrectus_width_scm))
191 porrectus_width = gh_scm2double (porrectus_width_scm);
193 else
195 porrectus_width = 2.4;
197 Real width = porrectus_width * Staff_symbol_referencer::staff_space (me);
199 if (String::compare (style, "vaticana") == 0)
200 molecule = brew_vaticana_molecule (me, interval,
201 solid, width, thickness,
202 add_stem, stem_direction);
203 else if (String::compare (style, "mensural") == 0)
204 molecule = brew_mensural_molecule (me, interval,
205 solid, width, thickness,
206 add_stem, stem_direction);
207 else
208 return SCM_EOL;
210 Real space = Staff_symbol_referencer::staff_space (me);
211 Real head_extent = molecule.extent (X_AXIS).length ();
212 Interval extent (-0.2 * head_extent, 1.2 * head_extent);
213 int interspaces = Staff_symbol_referencer::line_count (me)-1;
215 molecule.translate_axis (left_position_f * space/2, Y_AXIS);
217 int left_pos = (int)rint (left_position_f);
218 if (abs (left_pos) - interspaces > 1)
220 Molecule left_head_ledger_lines =
221 Note_head::brew_ledger_lines (me, left_pos, interspaces, extent, true);
222 left_head_ledger_lines.translate_axis (left_position_f * space/2,
223 Y_AXIS);
224 molecule.add_molecule (left_head_ledger_lines);
227 int right_pos = (int)rint (right_position_f);
228 if (abs (right_pos) - interspaces > 1)
230 Molecule right_head_ledger_lines =
231 Note_head::brew_ledger_lines (me, right_pos, interspaces, extent, true);
232 right_head_ledger_lines.translate_axis (right_position_f * space/2,
233 Y_AXIS);
234 molecule.add_molecule (right_head_ledger_lines);
237 return molecule.smobbed_copy();
240 Molecule
241 Porrectus::brew_vaticana_molecule (Item *me,
242 Real interval,
243 bool solid,
244 Real width,
245 Real thickness,
246 bool add_stem,
247 Direction stem_direction)
249 if (interval >= 0.0)
251 me->warning (_ ("ascending vaticana style porrectus"));
254 Real space = Staff_symbol_referencer::staff_space (me);
255 Molecule molecule = Molecule ();
256 Real right_height = 0.6 * space;
258 // Compensate thickness that appears to be smaller in steep section
259 // of bend.
260 Real left_height = right_height + min (0.12 * abs(interval), 0.3) * space;
262 if (add_stem)
264 bool consider_interval =
265 stem_direction * interval > 0.0;
267 Interval stem_box_x (0, thickness);
268 Interval stem_box_y;
270 if (consider_interval)
272 Real y_length = max (abs(interval)/2.0*space +
273 (right_height-left_height),
274 1.2*space);
275 stem_box_y = Interval (0, y_length);
277 else
278 stem_box_y = Interval (0, space);
280 Real y_correction =
281 (stem_direction == UP) ?
282 +0.5*left_height :
283 -0.5*left_height - stem_box_y.length();
285 Box stem_box (stem_box_x, stem_box_y);
286 Molecule stem = Lookup::filledbox (stem_box);
287 stem.translate_axis (y_correction, Y_AXIS);
288 molecule.add_molecule(stem);
291 // Compensate optical illusion regarding vertical position of left
292 // and right endings due to curved shape.
293 Real ypos_correction = -0.1*space * sign(interval);
294 Real interval_correction = 0.2*space * sign(interval);
295 Real corrected_interval = interval*space + interval_correction;
297 // middle curve of vaticana style porrectus
298 Bezier curve;
299 curve.control_[0] = Offset (0.00 * width, 0.0);
300 curve.control_[1] = Offset (0.33 * width, corrected_interval / 2.0);
301 curve.control_[2] = Offset (0.66 * width, corrected_interval / 2.0);
302 curve.control_[3] = Offset (1.00 * width, corrected_interval / 2.0);
304 Bezier top_curve = curve, bottom_curve = curve;
305 for (int i = 0; i < 4; i++)
307 Real thickness = 0.33 * ((3 - i)*left_height + i*right_height);
308 top_curve.control_[i] += Offset (0, +0.5*thickness);
309 bottom_curve.control_[i] += Offset (0, -0.5*thickness);
312 if (solid)
314 Molecule solid_head =
315 Lookup::bezier_sandwich (top_curve, bottom_curve);
316 molecule.add_molecule (solid_head);
318 else // outline
320 Bezier inner_top_curve = top_curve;
321 inner_top_curve.translate (Offset (0.0, -thickness));
322 Molecule top_edge =
323 Lookup::bezier_sandwich (top_curve, inner_top_curve);
324 molecule.add_molecule(top_edge);
326 Bezier inner_bottom_curve = bottom_curve;
327 inner_bottom_curve.translate (Offset (0.0, +thickness));
328 Molecule bottom_edge =
329 Lookup::bezier_sandwich (bottom_curve, inner_bottom_curve);
330 molecule.add_molecule(bottom_edge);
332 // TODO: Use horizontal slope with proper slope value rather
333 // than filled box for left edge, since the filled box stands
334 // out from the porrectus shape if the interval is big and the
335 // line thickness small. The difficulty here is to compute a
336 // proper slope value, as it should roughly be equal with the
337 // slope of the left end of the bezier curve.
338 Box left_edge_box (Interval (0, thickness),
339 Interval (-0.5*left_height, +0.5*left_height));
340 Molecule left_edge = Lookup::filledbox (left_edge_box);
341 molecule.add_molecule(left_edge);
343 Box right_edge_box (Interval (-thickness, 0),
344 Interval (-0.5*right_height, +0.5*right_height));
345 Molecule right_edge = Lookup::filledbox (right_edge_box);
346 right_edge.translate_axis (width, X_AXIS);
347 right_edge.translate_axis (corrected_interval / 2.0, Y_AXIS);
348 molecule.add_molecule(right_edge);
350 molecule.translate_axis (ypos_correction, Y_AXIS);
351 return molecule;
354 Molecule
355 Porrectus::brew_mensural_molecule (Item *me,
356 Real interval,
357 bool solid,
358 Real width,
359 Real thickness,
360 bool add_stem,
361 Direction stem_direction)
363 Real space = Staff_symbol_referencer::staff_space (me);
364 Real height = 0.6 * space;
365 Molecule molecule = Molecule ();
367 if (add_stem)
369 bool consider_interval =
370 stem_direction * interval > 0.0;
372 Interval stem_box_x (0, thickness);
373 Interval stem_box_y;
375 if (consider_interval)
377 Real y_length = max (interval/2.0*space, 1.2*space);
378 stem_box_y = Interval (0, y_length);
380 else
381 stem_box_y = Interval (0, space);
383 Real y_correction =
384 (stem_direction == UP) ?
385 +0.5*height :
386 -0.5*height - stem_box_y.length();
388 Box stem_box (stem_box_x, stem_box_y);
389 Molecule stem = Lookup::filledbox (stem_box);
390 stem.translate_axis (y_correction, Y_AXIS);
391 molecule.add_molecule(stem);
394 Real slope = (interval / 2.0 * space) / width;
396 // Compensate optical illusion regarding vertical position of left
397 // and right endings due to slope.
398 Real ypos_correction = -0.1*space * sign(slope);
399 Real slope_correction = 0.2*space * sign(slope);
400 Real corrected_slope = slope + slope_correction/width;
402 if (solid)
404 Molecule solid_head =
405 Lookup::horizontal_slope (width, corrected_slope, height);
406 molecule.add_molecule (solid_head);
408 else // outline
410 Molecule left_edge =
411 Lookup::horizontal_slope (thickness, corrected_slope, height);
412 molecule.add_molecule(left_edge);
414 Molecule right_edge =
415 Lookup::horizontal_slope (thickness, corrected_slope, height);
416 right_edge.translate_axis (width-thickness, X_AXIS);
417 right_edge.translate_axis (corrected_slope * (width-thickness), Y_AXIS);
418 molecule.add_molecule(right_edge);
420 Molecule bottom_edge =
421 Lookup::horizontal_slope (width, corrected_slope, thickness);
422 bottom_edge.translate_axis (-0.5*height, Y_AXIS);
423 molecule.add_molecule (bottom_edge);
425 Molecule top_edge =
426 Lookup::horizontal_slope (width, corrected_slope, thickness);
427 top_edge.translate_axis (+0.5*height, Y_AXIS);
428 molecule.add_molecule (top_edge);
430 molecule.translate_axis (ypos_correction, Y_AXIS);
431 return molecule;
435 ADD_INTERFACE (Porrectus,"porrectus-interface",
436 "A porrectus ligature, joining two note heads into a single grob.",
437 "left-head right-head width add-stem auto-properties solid direction");