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) 2004--2012 Han-Wen Nienhuys <hanwen@xs4all.nl> | 4 Copyright (C) 2004--2012 Han-Wen Nienhuys <hanwen@xs4all.nl> |
5 | 5 |
6 LilyPond is free software: you can redistribute it and/or modify | 6 LilyPond is free software: you can redistribute it and/or modify |
7 it under the terms of the GNU General Public License as published by | 7 it under the terms of the GNU General Public License as published by |
8 the Free Software Foundation, either version 3 of the License, or | 8 the Free Software Foundation, either version 3 of the License, or |
9 (at your option) any later version. | 9 (at your option) any later version. |
10 | 10 |
(...skipping 15 matching lines...) Expand all Loading... |
26 #include "note-column.hh" | 26 #include "note-column.hh" |
27 #include "paper-column.hh" | 27 #include "paper-column.hh" |
28 #include "pointer-group-interface.hh" | 28 #include "pointer-group-interface.hh" |
29 #include "warn.hh" | 29 #include "warn.hh" |
30 | 30 |
31 MAKE_SCHEME_CALLBACK (Self_alignment_interface, y_aligned_on_self, 1); | 31 MAKE_SCHEME_CALLBACK (Self_alignment_interface, y_aligned_on_self, 1); |
32 SCM | 32 SCM |
33 Self_alignment_interface::y_aligned_on_self (SCM element) | 33 Self_alignment_interface::y_aligned_on_self (SCM element) |
34 { | 34 { |
35 Grob *me = unsmob_grob (element); | 35 Grob *me = unsmob_grob (element); |
36 return align_grob (me, NULL, get_alignment (me, Y_AXIS), Y_AXIS, false, 0, 0); | 36 return align_grob (me, NULL, Y_AXIS, false, 0, 0); |
37 } | 37 } |
38 | 38 |
39 MAKE_SCHEME_CALLBACK (Self_alignment_interface, x_aligned_on_self, 1); | 39 MAKE_SCHEME_CALLBACK (Self_alignment_interface, x_aligned_on_self, 1); |
40 SCM | 40 SCM |
41 Self_alignment_interface::x_aligned_on_self (SCM element) | 41 Self_alignment_interface::x_aligned_on_self (SCM element) |
42 { | 42 { |
43 Grob *me = unsmob_grob (element); | 43 Grob *me = unsmob_grob (element); |
44 return align_grob (me, NULL, get_alignment (me, X_AXIS), X_AXIS, false, 0, 0); | 44 return align_grob (me, NULL, X_AXIS, false, 0, 0); |
45 } | 45 } |
46 | 46 |
47 MAKE_SCHEME_CALLBACK (Self_alignment_interface, pure_y_aligned_on_self, 3); | 47 MAKE_SCHEME_CALLBACK (Self_alignment_interface, pure_y_aligned_on_self, 3); |
48 SCM | 48 SCM |
49 Self_alignment_interface::pure_y_aligned_on_self (SCM smob, SCM start, SCM end) | 49 Self_alignment_interface::pure_y_aligned_on_self (SCM smob, SCM start, SCM end) |
50 { | 50 { |
51 Grob *me = unsmob_grob (smob); | 51 Grob *me = unsmob_grob (smob); |
52 return align_grob (me, NULL, get_alignment (me, Y_AXIS), Y_AXIS, true, robust_
scm2int (start, 0), robust_scm2int (end, INT_MAX)); | 52 return align_grob (me, NULL, Y_AXIS, true, robust_scm2int (start, 0), robust_s
cm2int (end, INT_MAX)); |
| 53 } |
| 54 |
| 55 SCM |
| 56 Self_alignment_interface::centered_on_object (Grob *him, Axis a) |
| 57 { |
| 58 return scm_from_double (robust_relative_extent (him, him, a).center ()); |
53 } | 59 } |
54 | 60 |
55 MAKE_SCHEME_CALLBACK (Self_alignment_interface, centered_on_x_parent, 1); | 61 MAKE_SCHEME_CALLBACK (Self_alignment_interface, centered_on_x_parent, 1); |
56 SCM | 62 SCM |
57 Self_alignment_interface::centered_on_x_parent (SCM smob) | 63 Self_alignment_interface::centered_on_x_parent (SCM smob) |
58 { | 64 { |
59 Grob *me = unsmob_grob (smob); | 65 return centered_on_object (unsmob_grob (smob)->get_parent (X_AXIS), X_AXIS); |
60 return align_grob (NULL, me->get_parent (X_AXIS), scm_from_double (0), X_AXIS,
false, 0, 0); | |
61 } | |
62 | |
63 MAKE_SCHEME_CALLBACK (Self_alignment_interface, centered_on_note_columns, 1); | |
64 SCM | |
65 Self_alignment_interface::centered_on_note_columns (SCM smob) | |
66 { | |
67 Item *it = unsmob_item (smob)->get_column (); | |
68 if (!it) | |
69 return scm_from_double (0.0); | |
70 | |
71 extract_grob_set (it, "elements", elts); | |
72 vector<Grob *> ncs; | |
73 Interval centers; | |
74 for (vsize i = 0; i < elts.size (); i++) | |
75 if (Note_column::has_interface (elts[i])) | |
76 centers.add_point (robust_relative_extent (elts[i], elts[i], X_AXIS).cente
r ()); | |
77 | |
78 if (centers.is_empty ()) | |
79 return scm_from_double (0.0); | |
80 | |
81 return scm_from_double (centers.center ()); | |
82 } | 66 } |
83 | 67 |
84 MAKE_SCHEME_CALLBACK (Self_alignment_interface, centered_on_y_parent, 1); | 68 MAKE_SCHEME_CALLBACK (Self_alignment_interface, centered_on_y_parent, 1); |
85 SCM | 69 SCM |
86 Self_alignment_interface::centered_on_y_parent (SCM smob) | 70 Self_alignment_interface::centered_on_y_parent (SCM smob) |
87 { | 71 { |
88 Grob *me = unsmob_grob (smob); | 72 return centered_on_object (unsmob_grob (smob)->get_parent (Y_AXIS), Y_AXIS); |
89 return align_grob (NULL, me->get_parent (Y_AXIS), scm_from_double (0), Y_AXIS,
false, 0, 0); | |
90 } | 73 } |
91 | 74 |
92 MAKE_SCHEME_CALLBACK (Self_alignment_interface, x_centered_on_y_parent, 1); | 75 MAKE_SCHEME_CALLBACK (Self_alignment_interface, x_centered_on_y_parent, 1); |
93 SCM | 76 SCM |
94 Self_alignment_interface::x_centered_on_y_parent (SCM smob) | 77 Self_alignment_interface::x_centered_on_y_parent (SCM smob) |
95 { | 78 { |
96 Grob *me = unsmob_grob (smob); | 79 return centered_on_object (unsmob_grob (smob)->get_parent (Y_AXIS), X_AXIS); |
97 return align_grob (NULL, me->get_parent (Y_AXIS), scm_from_double (0), X_AXIS,
false, 0, 0); | |
98 } | 80 } |
99 | 81 |
100 MAKE_SCHEME_CALLBACK (Self_alignment_interface, aligned_on_x_parent, 1); | 82 MAKE_SCHEME_CALLBACK (Self_alignment_interface, x_align_grob, 1); |
101 SCM | 83 SCM |
102 Self_alignment_interface::aligned_on_x_parent (SCM smob) | 84 Self_alignment_interface::x_align_grob (SCM smob) |
103 { | 85 { |
104 Grob *me = unsmob_grob (smob); | 86 Grob *me = unsmob_grob (smob); |
105 return align_grob (me, me->get_parent (X_AXIS), get_alignment (me, X_AXIS), X_
AXIS, false, 0, 0); | 87 return align_grob (me, me->get_parent (X_AXIS), X_AXIS, false, 0, 0); |
106 } | 88 } |
107 | 89 |
108 MAKE_SCHEME_CALLBACK (Self_alignment_interface, aligned_on_y_parent, 1); | 90 MAKE_SCHEME_CALLBACK (Self_alignment_interface, y_align_grob, 1); |
109 SCM | 91 SCM |
110 Self_alignment_interface::aligned_on_y_parent (SCM smob) | 92 Self_alignment_interface::y_align_grob (SCM smob) |
111 { | 93 { |
112 Grob *me = unsmob_grob (smob); | 94 Grob *me = unsmob_grob (smob); |
113 return align_grob (me, me->get_parent (Y_AXIS), get_alignment (me, Y_AXIS), Y_
AXIS, false, 0, 0); | 95 return align_grob (me, me->get_parent (Y_AXIS), Y_AXIS, false, 0, 0); |
114 } | |
115 | |
116 /* | |
117 This is not a part of align_grob because we might be unable to fetch | |
118 required properties there ('me' sometimes is a null pointer inside align_grob)
. | |
119 */ | |
120 SCM | |
121 Self_alignment_interface::get_alignment (Grob *me, Axis a) | |
122 { | |
123 return (a == X_AXIS) | |
124 ? me->internal_get_property (ly_symbol2scm ("self-alignment-X")) | |
125 : me->internal_get_property (ly_symbol2scm ("self-alignment-Y")); | |
126 } | 96 } |
127 | 97 |
128 /* | 98 /* |
129 Positioning of grobs is done relative to their parents in respective axes. | 99 Positioning of grobs is done relative to their parents in respective axes. |
130 Grob properties [XY]-offset measure the displacement between grob's reference | 100 Grob properties [XY]-offset measure the displacement between grob's reference |
131 point and the reference point of grob's parent in [XY]_AXIS (in staffspaces). | 101 point and the reference point of grob's parent in [XY]_AXIS (in staffspaces). |
132 | 102 |
133 To align a particular point of the grob with a particular point of its parent | 103 To align a particular point of the grob with a particular point of its parent |
134 one has to calculate what this offset should be, based on dimensions (extents) | 104 one has to calculate what this offset should be, based on dimensions (extents) |
135 of both objects. | 105 of both objects. |
136 */ | 106 */ |
137 SCM | 107 SCM |
138 Self_alignment_interface::align_grob (Grob *me, | 108 Self_alignment_interface::align_grob (Grob *me, |
139 Grob *him, | 109 Grob *him, |
140 SCM alignment, | |
141 Axis a, | 110 Axis a, |
142 bool pure, | 111 bool pure, |
143 int start, | 112 int start, |
144 int end) | 113 int end) |
145 { | 114 { |
146 Real offset = 0.0; | 115 Real offset = 0.0; |
147 | 116 |
| 117 SCM alignment = (a == X_AXIS) |
| 118 ? me->internal_get_property (ly_symbol2scm ("self-alignment-X"
)) |
| 119 : me->internal_get_property (ly_symbol2scm ("self-alignment-Y"
)); |
| 120 |
| 121 SCM my_alignment, his_alignment; |
| 122 if (scm_is_pair (alignment)) |
| 123 { |
| 124 my_alignment = scm_car (alignment); |
| 125 his_alignment = scm_cdr (alignment); |
| 126 } |
| 127 else |
| 128 { |
| 129 my_alignment = alignment; |
| 130 his_alignment = alignment; |
| 131 } |
| 132 |
148 // calculate offset related to grob's own dimensions | 133 // calculate offset related to grob's own dimensions |
149 if (me && scm_is_number (alignment)) | 134 if (scm_is_number (my_alignment)) |
150 { | 135 { |
151 Interval my_ext (me->maybe_pure_extent (me, a, pure, start, end)); | 136 Interval my_ext = me->maybe_pure_extent (me, a, pure, start, end); |
152 if (my_ext.is_empty ()) | 137 |
153 programming_error ("cannot align on self: empty element"); | 138 // Empty extent doesn't mean an error - we simply don't align such grobs. |
154 else | 139 // However, empty extent and non-empty stencil would be suspicious. |
155 offset -= my_ext.linear_combination (scm_to_double (alignment)); | 140 if (!my_ext.is_empty ()) |
| 141 offset -= my_ext.linear_combination (scm_to_double (my_alignment)); |
| 142 else if (me->get_stencil ()) |
| 143 warning (me->name () + " has empty extent and non-empty stencil."); |
156 } | 144 } |
157 | 145 |
158 // calculate offset related to grob's parent dimensions | 146 // calculate offset related to grob's parent dimensions |
159 if (him && scm_is_number (alignment)) | 147 if (him && scm_is_number (his_alignment)) |
160 { | 148 { |
| 149 Interval his_ext; |
161 if (Paper_column::has_interface (him)) | 150 if (Paper_column::has_interface (him)) |
162 return scm_from_double (0.0); | 151 /* |
| 152 PaperColumn extents aren't reliable (they depend on size and alignment |
| 153 of PaperColumn's children), so we cannot use them. Instead, we extract |
| 154 the extent of a respective NoteColumn and align on it. |
| 155 This situation (i.e. having PaperColumn as parent) happens for example |
| 156 for unassociated lyrics (i.e. lyrics without associatedVoice set), |
| 157 or dynamics attached to spacers. |
| 158 */ |
| 159 his_ext = Paper_column::get_generic_interface_extent |
| 160 (him, ly_symbol2scm ("note-column-interface"), a); |
| 161 else |
| 162 his_ext = him->maybe_pure_extent (him, a, pure, start, end); |
163 | 163 |
164 Interval his_ext = him->maybe_pure_extent (him, a, pure, start, end); | 164 // Empty extent doesn't mean an error - we simply don't align such grobs. |
| 165 // However, empty extent and non-empty stencil would be suspicious. |
165 if (!his_ext.is_empty ()) | 166 if (!his_ext.is_empty ()) |
166 offset += his_ext.linear_combination (scm_to_double (alignment)); | 167 offset += his_ext.linear_combination (scm_to_double (his_alignment)); |
| 168 else if (him->get_stencil ()) |
| 169 warning (him->name () + " has empty extent and non-empty stencil."); |
167 } | 170 } |
168 | 171 |
169 return scm_from_double (offset); | 172 return scm_from_double (offset); |
170 } | |
171 | |
172 void | |
173 Self_alignment_interface::set_center_parent (Grob *me, Axis a) | |
174 { | |
175 add_offset_callback (me, | |
176 (a == X_AXIS) ? centered_on_x_parent_proc : centered_on_y
_parent_proc, | |
177 a); | |
178 } | |
179 | |
180 void | |
181 Self_alignment_interface::set_align_self (Grob *me, Axis a) | |
182 { | |
183 add_offset_callback (me, | |
184 (a == X_AXIS) ? x_aligned_on_self_proc : y_aligned_on_sel
f_proc, | |
185 a); | |
186 } | 173 } |
187 | 174 |
188 ADD_INTERFACE (Self_alignment_interface, | 175 ADD_INTERFACE (Self_alignment_interface, |
189 "Align a particular point of this object on" | 176 "Align a particular point of this object on" |
190 " a particular point of its parent, for example" | 177 " a particular point of its parent, for example" |
191 " object's reference point on its parent's center." | 178 " object's reference point on its parent's center." |
192 " Alignment is read from properties @code{self-alignment-X}" | 179 " Alignment is read from properties @code{self-alignment-X}" |
193 " and @code{self-alignment-Y}.", | 180 " and @code{self-alignment-Y}.", |
194 | 181 |
195 /* properties */ | 182 /* properties */ |
196 "collision-bias " | 183 "collision-bias " |
197 "collision-padding " | 184 "collision-padding " |
198 "potential-X-colliding-grobs " | 185 "potential-X-colliding-grobs " |
199 "self-alignment-X " | 186 "self-alignment-X " |
200 "self-alignment-Y " | 187 "self-alignment-Y " |
201 "X-colliding-grobs " | 188 "X-colliding-grobs " |
202 "Y-colliding-grobs " | 189 "Y-colliding-grobs " |
203 ); | 190 ); |
LEFT | RIGHT |