Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2023 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "singlelayout.h"
24 :
25 : #include "draw/fontmetrics.h"
26 :
27 : #include "types/types.h"
28 : #include "types/typesconv.h"
29 : #include "types/symnames.h"
30 :
31 : #include "dom/dummyparent.h"
32 :
33 : #include "dom/factory.h"
34 :
35 : #include "dom/engravingitem.h"
36 : #include "dom/score.h"
37 :
38 : #include "dom/accidental.h"
39 : #include "dom/actionicon.h"
40 : #include "dom/ambitus.h"
41 : #include "dom/articulation.h"
42 : #include "dom/bagpembell.h"
43 : #include "dom/barline.h"
44 : #include "dom/bend.h"
45 : #include "dom/bracket.h"
46 : #include "dom/breath.h"
47 : #include "dom/capo.h"
48 : #include "dom/chordline.h"
49 : #include "dom/clef.h"
50 : #include "dom/dynamic.h"
51 : #include "dom/expression.h"
52 : #include "dom/fermata.h"
53 : #include "dom/fingering.h"
54 : #include "dom/fret.h"
55 : #include "dom/glissando.h"
56 : #include "dom/gradualtempochange.h"
57 : #include "dom/guitarbend.h"
58 : #include "dom/hairpin.h"
59 : #include "dom/hammeronpulloff.h"
60 : #include "dom/harppedaldiagram.h"
61 : #include "dom/instrchange.h"
62 : #include "dom/jump.h"
63 : #include "dom/keysig.h"
64 : #include "dom/letring.h"
65 : #include "dom/line.h"
66 : #include "dom/lyrics.h"
67 : #include "dom/marker.h"
68 : #include "dom/measurenumber.h"
69 : #include "dom/measurerepeat.h"
70 : #include "dom/note.h"
71 : #include "dom/noteline.h"
72 : #include "dom/ornament.h"
73 : #include "dom/ottava.h"
74 : #include "dom/page.h"
75 : #include "dom/palmmute.h"
76 : #include "dom/pedal.h"
77 : #include "dom/playcounttext.h"
78 : #include "dom/playtechannotation.h"
79 : #include "dom/rehearsalmark.h"
80 : #include "dom/slur.h"
81 : #include "dom/soundflag.h"
82 : #include "dom/spacer.h"
83 : #include "dom/staff.h"
84 : #include "dom/stafftext.h"
85 : #include "dom/stafftypechange.h"
86 : #include "dom/sticking.h"
87 : #include "dom/stringtunings.h"
88 : #include "dom/symbol.h"
89 : #include "dom/systemtext.h"
90 : #include "dom/tapping.h"
91 : #include "dom/tempotext.h"
92 : #include "dom/text.h"
93 : #include "dom/textline.h"
94 : #include "dom/textlinebase.h"
95 : #include "dom/timesig.h"
96 : #include "dom/tremolobar.h"
97 : #include "dom/tremolosinglechord.h"
98 : #include "dom/tremolotwochord.h"
99 : #include "dom/trill.h"
100 : #include "dom/vibrato.h"
101 : #include "dom/volta.h"
102 : #include "dom/whammybar.h"
103 :
104 : #include "dom/utils.h"
105 :
106 : #include "rendering/score/tlayout.h"
107 : #include "rendering/score/textlayout.h"
108 : #include "rendering/score/tremololayout.h"
109 : #include "rendering/score/chordlayout.h"
110 : #include "rendering/score/slurtielayout.h"
111 :
112 : #include "log.h"
113 :
114 : using namespace muse;
115 : using namespace muse::draw;
116 : using namespace mu::engraving;
117 : using namespace mu::engraving::rendering::score;
118 : using namespace mu::engraving::rendering::single;
119 :
120 0 : void SingleLayout::layoutItem(EngravingItem* item)
121 : {
122 0 : Context ctx(item->score());
123 :
124 0 : switch (item->type()) {
125 0 : case ElementType::ACCIDENTAL: layout(toAccidental(item), ctx);
126 0 : break;
127 0 : case ElementType::ACTION_ICON: layout(toActionIcon(item), ctx);
128 0 : break;
129 0 : case ElementType::AMBITUS: layout(toAmbitus(item), ctx);
130 0 : break;
131 0 : case ElementType::ARPEGGIO: layout(toArpeggio(item), ctx);
132 0 : break;
133 0 : case ElementType::CHORD_BRACKET: layout(toChordBracket(item), ctx);
134 0 : break;
135 0 : case ElementType::ARTICULATION: layout(toArticulation(item), ctx);
136 0 : break;
137 0 : case ElementType::BAGPIPE_EMBELLISHMENT: layout(toBagpipeEmbellishment(item), ctx);
138 0 : break;
139 0 : case ElementType::BAR_LINE: layout(toBarLine(item), ctx);
140 0 : break;
141 0 : case ElementType::BEND: layout(toBend(item), ctx);
142 0 : break;
143 0 : case ElementType::BRACKET: layout(toBracket(item), ctx);
144 0 : break;
145 0 : case ElementType::BREATH: layout(toBreath(item), ctx);
146 0 : break;
147 0 : case ElementType::CAPO: layout(toCapo(item), ctx);
148 0 : break;
149 0 : case ElementType::CHORDLINE: layout(toChordLine(item), ctx);
150 0 : break;
151 0 : case ElementType::CLEF: layout(toClef(item), ctx);
152 0 : break;
153 0 : case ElementType::DYNAMIC: layout(toDynamic(item), ctx);
154 0 : break;
155 0 : case ElementType::EXPRESSION: layout(toExpression(item), ctx);
156 0 : break;
157 0 : case ElementType::FERMATA: layout(toFermata(item), ctx);
158 0 : break;
159 0 : case ElementType::FINGERING: layout(toFingering(item), ctx);
160 0 : break;
161 0 : case ElementType::FRET_DIAGRAM: layout(toFretDiagram(item), ctx);
162 0 : break;
163 0 : case ElementType::FSYMBOL: layout(toFSymbol(item), ctx);
164 0 : break;
165 0 : case ElementType::GLISSANDO: layout(toGlissando(item), ctx);
166 0 : break;
167 0 : case ElementType::GUITAR_BEND: layout(toGuitarBend(item), ctx);
168 0 : break;
169 0 : case ElementType::GRADUAL_TEMPO_CHANGE: layout(toGradualTempoChange(item), ctx);
170 0 : break;
171 0 : case ElementType::HAIRPIN: layout(toHairpin(item), ctx);
172 0 : break;
173 0 : case ElementType::HAMMER_ON_PULL_OFF: layout(toHammerOnPullOff(item), ctx);
174 0 : break;
175 0 : case ElementType::HARP_DIAGRAM: layout(toHarpPedalDiagram(item), ctx);
176 0 : break;
177 0 : case ElementType::IMAGE: layout(toImage(item), ctx);
178 0 : break;
179 0 : case ElementType::INSTRUMENT_CHANGE: layout(toInstrumentChange(item), ctx);
180 0 : break;
181 0 : case ElementType::JUMP: layout(toJump(item), ctx);
182 0 : break;
183 0 : case ElementType::KEYSIG: layout(toKeySig(item), ctx);
184 0 : break;
185 0 : case ElementType::LAYOUT_BREAK: layout(toLayoutBreak(item), ctx);
186 0 : break;
187 0 : case ElementType::LET_RING: layout(toLetRing(item), ctx);
188 0 : break;
189 0 : case ElementType::LYRICS: layout(toLyrics(item), ctx);
190 0 : break;
191 0 : case ElementType::MARKER: layout(toMarker(item), ctx);
192 0 : break;
193 0 : case ElementType::MEASURE_NUMBER: layout(toMeasureNumber(item), ctx);
194 0 : break;
195 0 : case ElementType::MEASURE_REPEAT: layout(toMeasureRepeat(item), ctx);
196 0 : break;
197 0 : case ElementType::NOTEHEAD: layout(toNoteHead(item), ctx);
198 0 : break;
199 0 : case ElementType::NOTELINE: layout(toNoteLine(item), ctx);
200 0 : break;
201 0 : case ElementType::OTTAVA: layout(toOttava(item), ctx);
202 0 : break;
203 0 : case ElementType::ORNAMENT: layout(toOrnament(item), ctx);
204 0 : break;
205 0 : case ElementType::PALM_MUTE: layout(toPalmMute(item), ctx);
206 0 : break;
207 0 : case ElementType::PEDAL: layout(toPedal(item), ctx);
208 0 : break;
209 0 : case ElementType::PLAY_COUNT_TEXT: layout(toPlayCountText(item), ctx);
210 0 : break;
211 0 : case ElementType::PLAYTECH_ANNOTATION: layout(toPlayTechAnnotation(item), ctx);
212 0 : break;
213 0 : case ElementType::REHEARSAL_MARK: layout(toRehearsalMark(item), ctx);
214 0 : break;
215 0 : case ElementType::SLUR: layout(toSlur(item), ctx);
216 0 : break;
217 0 : case ElementType::SPACER: layout(toSpacer(item), ctx);
218 0 : break;
219 0 : case ElementType::STAFF_TEXT: layout(toStaffText(item), ctx);
220 0 : break;
221 0 : case ElementType::STAVE_SHARING_LABEL: layout(toStaveSharingLabel(item), ctx);
222 0 : break;
223 0 : case ElementType::STAFFTYPE_CHANGE: layout(toStaffTypeChange(item), ctx);
224 0 : break;
225 0 : case ElementType::STICKING: layout(toSticking(item), ctx);
226 0 : break;
227 0 : case ElementType::STRING_TUNINGS: layout(toStringTunings(item), ctx);
228 0 : break;
229 0 : case ElementType::SYMBOL: layout(toSymbol(item), ctx);
230 0 : break;
231 0 : case ElementType::SYSTEM_TEXT: layout(toSystemText(item), ctx);
232 0 : break;
233 0 : case ElementType::SOUND_FLAG: layout(item_cast<SoundFlag*>(item), ctx);
234 0 : break;
235 0 : case ElementType::TAPPING: layout(toTapping(item), ctx);
236 0 : break;
237 0 : case ElementType::TEMPO_TEXT: layout(toTempoText(item), ctx);
238 0 : break;
239 0 : case ElementType::TEXT: layout(toText(item), ctx);
240 0 : break;
241 0 : case ElementType::TEXTLINE: layout(toTextLine(item), ctx);
242 0 : break;
243 0 : case ElementType::TIMESIG: layout(toTimeSig(item), ctx);
244 0 : break;
245 0 : case ElementType::TREMOLO_SINGLECHORD: layout(item_cast<TremoloSingleChord*>(item), ctx);
246 0 : break;
247 0 : case ElementType::TREMOLO_TWOCHORD: layout(item_cast<TremoloTwoChord*>(item), ctx);
248 0 : break;
249 0 : case ElementType::TREMOLOBAR: layout(toTremoloBar(item), ctx);
250 0 : break;
251 0 : case ElementType::TRILL: layout(toTrill(item), ctx);
252 0 : break;
253 0 : case ElementType::VIBRATO: layout(toVibrato(item), ctx);
254 0 : break;
255 0 : case ElementType::VOLTA: layout(toVolta(item), ctx);
256 0 : break;
257 0 : case ElementType::WHAMMY_BAR: layout(toWhammyBar(item), ctx);
258 0 : break;
259 : // drumset
260 0 : case ElementType::CHORD: layout(toChord(item), ctx);
261 0 : break;
262 0 : case ElementType::STEM: layout(toStem(item), ctx);
263 0 : break;
264 0 : default:
265 0 : LOGE() << "Not handled: " << item->typeName();
266 0 : IF_ASSERT_FAILED(false) {
267 : LayoutContext tctx(item->score());
268 : TLayout::layoutItem(item, tctx);
269 : }
270 : break;
271 : }
272 0 : }
273 :
274 0 : void SingleLayout::layoutLineSegment(LineSegment* item, const Context& ctx)
275 : {
276 0 : switch (item->type()) {
277 0 : case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT: layout(toGradualTempoChangeSegment(item), ctx);
278 0 : break;
279 0 : case ElementType::HAIRPIN_SEGMENT: layout(toHairpinSegment(item), ctx);
280 0 : break;
281 0 : case ElementType::LET_RING_SEGMENT: layout(toLetRingSegment(item), ctx);
282 0 : break;
283 0 : case ElementType::OTTAVA_SEGMENT: layout(toOttavaSegment(item), ctx);
284 0 : break;
285 0 : case ElementType::PALM_MUTE_SEGMENT: layout(toPalmMuteSegment(item), ctx);
286 0 : break;
287 0 : case ElementType::PEDAL_SEGMENT: layout(toPedalSegment(item), ctx);
288 0 : break;
289 0 : case ElementType::TEXTLINE_SEGMENT: layout(toTextLineSegment(item), ctx);
290 0 : break;
291 0 : case ElementType::TRILL_SEGMENT: layout(toTrillSegment(item), ctx);
292 0 : break;
293 0 : case ElementType::VIBRATO_SEGMENT: layout(toVibratoSegment(item), ctx);
294 0 : break;
295 0 : case ElementType::VOLTA_SEGMENT: layout(toVoltaSegment(item), ctx);
296 0 : break;
297 0 : case ElementType::WHAMMY_BAR_SEGMENT: layout(toWhammyBarSegment(item), ctx);
298 0 : break;
299 0 : default:
300 0 : UNREACHABLE;
301 : break;
302 : }
303 0 : }
304 :
305 0 : const MStyle& SingleLayout::Context::style() const
306 : {
307 0 : return m_score->style();
308 : }
309 :
310 0 : std::shared_ptr<IEngravingFont> SingleLayout::Context::engravingFont() const
311 : {
312 0 : return m_score->engravingFont();
313 : }
314 :
315 0 : DummyParent* SingleLayout::Context::dummyParent() const
316 : {
317 0 : return m_score->dummy();
318 : }
319 :
320 0 : void SingleLayout::layout(Accidental* item, const Context&)
321 : {
322 0 : if (!item->ldata() || !item->ldata()->isValid()) {
323 0 : Accidental::LayoutData* ldata = item->mutldata();
324 0 : SymId symId = item->symId();
325 0 : Accidental::LayoutData::Sym s(symId, 0.0, 0.0);
326 0 : ldata->syms.push_back(s);
327 0 : ldata->setBbox(item->symBbox(symId));
328 : }
329 0 : }
330 :
331 0 : void SingleLayout::layout(ActionIcon* item, const Context&)
332 : {
333 0 : FontMetrics fontMetrics(item->iconFont());
334 0 : item->setbbox(fontMetrics.boundingRect(Char(item->icon())));
335 0 : }
336 :
337 0 : void SingleLayout::layout(Ambitus* item, const Context& ctx)
338 : {
339 0 : Ambitus::LayoutData* ldata = item->mutldata();
340 0 : double headWdt = item->headWidth();
341 0 : double spatium = item->spatium();
342 :
343 0 : double lineDist = spatium;
344 0 : constexpr int numOfLines = 3;
345 :
346 : //
347 : // NOTEHEADS Y POS
348 : //
349 : // if pitch == INVALID_PITCH or tpc == Tpc::TPC_INVALID, set to some default:
350 : // for use in palettes and when actual range cannot be calculated (new ambitus or no notes in staff)
351 : //
352 :
353 : // top notehead
354 0 : ldata->topPos.setY(0.0);
355 :
356 : // bottom notehead
357 0 : ldata->bottomPos.setY((numOfLines - 1) * lineDist);
358 :
359 : //
360 : // NOTEHEAD X POS
361 : //
362 : // Note: manages colliding accidentals
363 : //
364 :
365 0 : layout(item->topAccidental(), ctx);
366 0 : layout(item->bottomAccidental(), ctx);
367 :
368 0 : double accNoteDist = item->absoluteFromSpatium(ctx.style().styleS(Sid::accidentalNoteDistance));
369 0 : double xAccidOffTop = item->topAccidental()->width(LD_ACCESS::BAD) + accNoteDist;
370 0 : double xAccidOffBottom = item->bottomAccidental()->width(LD_ACCESS::BAD) + accNoteDist;
371 :
372 0 : switch (item->direction()) {
373 0 : case DirectionH::AUTO: // noteheads one above the other
374 : // left align noteheads and right align accidentals 'hanging' on the left
375 0 : ldata->topPos.setX(0.0);
376 0 : ldata->bottomPos.setX(0.0);
377 0 : item->topAccidental()->mutldata()->setPosX(-xAccidOffTop);
378 0 : item->bottomAccidental()->mutldata()->setPosX(-xAccidOffBottom);
379 0 : break;
380 0 : case DirectionH::LEFT: // top notehead at the left of bottom notehead
381 : // place top notehead at left margin; bottom notehead at right of top head;
382 : // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
383 0 : ldata->topPos.setX(0.0);
384 0 : ldata->bottomPos.setX(headWdt);
385 0 : item->topAccidental()->mutldata()->setPosX(-xAccidOffTop);
386 0 : item->bottomAccidental()->mutldata()->setPosX(headWdt - xAccidOffBottom);
387 0 : break;
388 0 : case DirectionH::RIGHT: // top notehead at the right of bottom notehead
389 : // bottom notehead at left margin; top notehead at right of bottomnotehead
390 : // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
391 0 : ldata->bottomPos.setX(0.0);
392 0 : ldata->topPos.setX(headWdt);
393 0 : item->bottomAccidental()->mutldata()->setPosX(-xAccidOffBottom);
394 0 : item->topAccidental()->mutldata()->setPosX(headWdt - xAccidOffTop);
395 0 : break;
396 : }
397 :
398 : // compute line from top note centre to bottom note centre
399 0 : LineF fullLine(ldata->topPos.x() + headWdt * 0.5,
400 : ldata->topPos.y(),
401 0 : ldata->bottomPos.x() + headWdt * 0.5,
402 0 : ldata->bottomPos.y());
403 : // shorten line on each side by offsets
404 0 : double yDelta = ldata->bottomPos.y() - ldata->topPos.y();
405 0 : if (!RealIsNull(yDelta)) {
406 0 : double off = Ambitus::LINEOFFSET_DEFAULT.toAbsolute(spatium);
407 0 : PointF p1 = fullLine.pointAt(off / yDelta);
408 0 : PointF p2 = fullLine.pointAt(1 - (off / yDelta));
409 0 : ldata->line = LineF(p1, p2);
410 : } else {
411 0 : ldata->line = fullLine;
412 : }
413 :
414 0 : RectF headRect(0, -0.5 * spatium, headWdt, 1 * spatium);
415 0 : ldata->setBbox(headRect.translated(ldata->topPos).united(headRect.translated(ldata->bottomPos))
416 0 : .united(item->topAccidental()->ldata()->bbox(LD_ACCESS::BAD)
417 0 : .translated(item->topAccidental()->ldata()->pos()))
418 0 : .united(item->bottomAccidental()->ldata()->bbox(LD_ACCESS::BAD)
419 0 : .translated(item->bottomAccidental()->ldata()->pos()))
420 : );
421 0 : }
422 :
423 0 : void SingleLayout::layout(Arpeggio* item, const Context& ctx)
424 : {
425 0 : Arpeggio::LayoutData* ldata = item->mutldata();
426 :
427 0 : auto symbolLine = [](const std::shared_ptr<const IEngravingFont>& f, Arpeggio::LayoutData* data, SymId end, SymId fill)
428 : {
429 0 : data->symbols.clear();
430 :
431 0 : double w = data->bottom - data->top;
432 0 : double w1 = f->advance(end, data->magS);
433 0 : double w2 = f->advance(fill, data->magS);
434 0 : int n = lrint((w - w1) / w2);
435 0 : for (int i = 0; i < n; ++i) {
436 0 : data->symbols.push_back(fill);
437 : }
438 0 : data->symbols.push_back(end);
439 0 : };
440 :
441 0 : ldata->arpeggioHeight = item->spatium() * 4;
442 0 : ldata->top = 0.0;
443 0 : ldata->bottom = ldata->arpeggioHeight;
444 :
445 0 : ldata->setMag(item->staff() ? item->staff()->staffMag(item->tick()) : item->mag());
446 0 : ldata->magS = ldata->mag() * (ctx.style().spatium() / ctx.style().defaultSpatium());
447 :
448 0 : std::shared_ptr<const IEngravingFont> font = ctx.engravingFont();
449 0 : switch (item->arpeggioType()) {
450 0 : case ArpeggioType::NORMAL: {
451 0 : symbolLine(font, ldata, SymId::wiggleArpeggiatoUp, SymId::wiggleArpeggiatoUp);
452 : // string is rotated -90 degrees
453 0 : ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
454 0 : ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
455 0 : } break;
456 :
457 0 : case ArpeggioType::UP: {
458 0 : symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
459 : // string is rotated -90 degrees
460 0 : ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
461 0 : ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
462 0 : } break;
463 :
464 0 : case ArpeggioType::DOWN: {
465 0 : symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
466 : // string is rotated +90 degrees (so that UpArrow turns into a DownArrow)
467 0 : ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
468 0 : ldata->setBbox(RectF(0.0, ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
469 0 : } break;
470 :
471 0 : case ArpeggioType::UP_STRAIGHT: {
472 0 : double x1 = item->spatium() * 0.5;
473 0 : ldata->symsBBox = font->bbox(SymId::arrowheadBlackUp, ldata->magS);
474 0 : double w = ldata->symsBBox.width();
475 0 : ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
476 0 : } break;
477 :
478 0 : case ArpeggioType::DOWN_STRAIGHT: {
479 0 : double x1 = item->spatium() * 0.5;
480 0 : ldata->symsBBox = font->bbox(SymId::arrowheadBlackDown, ldata->magS);
481 0 : double w = ldata->symsBBox.width();
482 0 : ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
483 0 : } break;
484 :
485 0 : case ArpeggioType::BRACKET: {
486 0 : double w = ctx.style().styleS(Sid::arpeggioHookLen).val() * item->spatium();
487 0 : ldata->setBbox(RectF(0.0, ldata->top, w, ldata->bottom));
488 0 : } break;
489 : }
490 0 : }
491 :
492 0 : void SingleLayout::layout(ChordBracket* item, const Context& ctx)
493 : {
494 0 : ChordBracket::LayoutData* ldata = item->mutldata();
495 :
496 0 : ldata->arpeggioHeight = item->spatium() * 2;
497 0 : ldata->top = 0.0;
498 0 : ldata->bottom = ldata->arpeggioHeight;
499 0 : ldata->setMag(1);
500 0 : ldata->magS = 1;
501 :
502 0 : double w = item->absoluteFromSpatium(ctx.style().styleS(Sid::chordBracketHookLen));
503 0 : ldata->setBbox(RectF(0.0, ldata->top, w, ldata->bottom));
504 0 : }
505 :
506 0 : void SingleLayout::layout(Articulation* item, const Context& ctx)
507 : {
508 0 : RectF bbox;
509 :
510 0 : if (item->textType() != ArticulationTextType::NO_TEXT) {
511 0 : if (!item->text()) {
512 0 : Text* text = new Text(item, TextStyleType::ARTICULATION);
513 0 : static const ElementStyle elementStyle = {};
514 0 : text->initElementStyle(&elementStyle);
515 0 : item->setText(text);
516 : }
517 :
518 0 : Text* text = item->text();
519 0 : text->setXmlText(TConv::text(item->textType()));
520 0 : text->setTrack(item->track());
521 0 : text->setOwnershipParent(item);
522 :
523 0 : layoutTextBase(item->text(), ctx, item->text()->mutldata());
524 0 : bbox = text->ldata()->bbox();
525 : } else {
526 0 : bbox = item->symBbox(item->symId());
527 0 : bbox.translate(-0.5 * bbox.width(), 0.0);
528 : }
529 :
530 0 : item->setbbox(bbox);
531 0 : }
532 :
533 0 : void SingleLayout::layout(BagpipeEmbellishment* item, const Context& ctx)
534 : {
535 0 : BagpipeEmbellishment::LayoutData* ldata = item->mutldata();
536 0 : const double mags = item->magS() * 0.75; // grace head magnification
537 0 : const double spatium = ctx.style().spatium();
538 :
539 0 : const BagpipeNoteList nl = item->resolveNoteList();
540 :
541 0 : ldata->clearBbox();
542 0 : ldata->isDrawBeam = nl.size() > 1;
543 0 : ldata->isDrawFlag = nl.size() == 1;
544 0 : ldata->spatium = spatium;
545 0 : ldata->headsym = SymId::noteheadBlack;
546 0 : ldata->flagsym = SymId::flag32ndUp;
547 0 : ldata->stemLineW = ldata->spatium * 0.1;
548 :
549 0 : BagpipeEmbellishment::LayoutData::BeamData& dataBeam = ldata->beamData;
550 0 : dataBeam.y = (-8 * spatium / 2);
551 0 : dataBeam.width = (0.3 * spatium);
552 :
553 0 : const RectF headBBox = ctx.engravingFont()->bbox(ldata->headsym, mags);
554 0 : const RectF flagBBox = ctx.engravingFont()->bbox(ldata->flagsym, mags);
555 0 : const double xcorr = spatium * 0.1; // correction to align flag with top of stem
556 :
557 0 : double headW = headBBox.width();
558 0 : const double headw = 1.2 * headW; // grace head width
559 0 : const double headp = 1.6 * headW; // horizontal head pitch
560 0 : const double xl = (1 - 1.6 * (nl.size() - 1)) * headW / 2; // calc x for stem of leftmost note
561 :
562 : // draw the notes including stem, (optional) flag and (optional) ledger line
563 0 : double x = xl;
564 0 : for (size_t i = 0; i < nl.size(); ++i) {
565 0 : BagpipeEmbellishment::LayoutData::NoteData& noteData = ldata->notesData[i];
566 0 : const int line = BagpipeEmbellishment::BAGPIPE_NOTEINFO_LIST[nl.at(i)].line;
567 0 : const double y1f = ((line - 6) * spatium / 2); // top of stem for note with flag
568 0 : const double y2 = (line * spatium / 2); // bottom of stem
569 0 : const double ycorr = (0.8 * spatium); // correction to align flag with top of stem
570 :
571 : // head
572 0 : noteData.headXY = PointF(x - headw, y2);
573 0 : ldata->addBbox(headBBox.translated(noteData.headXY));
574 :
575 : // stem
576 : // top of stems actually used
577 0 : double y1 = ldata->isDrawFlag ? y1f : dataBeam.y;
578 0 : noteData.stemLine = LineF(x - ldata->stemLineW * .5, y1, x - ldata->stemLineW * .5, y2);
579 0 : ldata->addBbox(RectF(x - ldata->stemLineW * .5 - headw, y1, ldata->stemLineW, y2 - y1));
580 :
581 : // flag
582 0 : if (ldata->isDrawFlag) {
583 0 : noteData.flagXY = PointF(x - ldata->stemLineW * .5 + xcorr, y1 + ycorr);
584 0 : ldata->addBbox(flagBBox.translated(noteData.flagXY));
585 : }
586 :
587 : // draw the ledger line for high A
588 0 : if (line == -2) {
589 0 : noteData.ledgerLine = LineF(x - headw * 1.5 - ldata->stemLineW * .5, y2, x + headw * .5 - ldata->stemLineW * .5, y2);
590 0 : ldata->addBbox(RectF(x - headw * 1.5 - ldata->stemLineW * .5, y2 - ldata->stemLineW * 2, headw * 2, ldata->stemLineW));
591 : }
592 :
593 : // move x to next note x position
594 0 : x += headp;
595 : }
596 :
597 0 : dataBeam.x1 = xl - ldata->stemLineW * .5;
598 0 : dataBeam.x2 = x - headp - ldata->stemLineW * .5;
599 0 : }
600 :
601 0 : void SingleLayout::layout(BarLine* item, const Context& ctx)
602 : {
603 0 : BarLine::LayoutData* ldata = item->mutldata();
604 0 : ldata->setPos(PointF());
605 0 : ldata->setMag(1.0);
606 :
607 0 : double spatium = item->spatium();
608 0 : ldata->y1 = (spatium * .5 * item->spanFrom());
609 0 : if (muse::RealIsEqual(ldata->y2, 0.0)) {
610 0 : ldata->y2 = (spatium * .5 * (8.0 + item->spanTo()));
611 : }
612 :
613 0 : auto layoutWidth = [](BarLine* item, const Context& ctx) {
614 0 : const double dotWidth = item->symWidth(SymId::repeatDot);
615 :
616 0 : double w = 0.0;
617 0 : switch (item->barLineType()) {
618 0 : case BarLineType::DOUBLE:
619 0 : w = ctx.style().styleAbsolute(Sid::doubleBarWidth) * 2.0 + ctx.style().styleAbsolute(Sid::doubleBarDistance);
620 0 : break;
621 0 : case BarLineType::DOUBLE_HEAVY:
622 0 : w = ctx.style().styleAbsolute(Sid::endBarWidth) * 2.0 + ctx.style().styleAbsolute(Sid::endBarDistance);
623 0 : break;
624 0 : case BarLineType::END_START_REPEAT:
625 0 : w = ctx.style().styleAbsolute(Sid::endBarWidth)
626 0 : + ctx.style().styleAbsolute(Sid::barWidth) * 2.0
627 0 : + ctx.style().styleAbsolute(Sid::endBarDistance) * 2.0
628 0 : + ctx.style().styleAbsolute(Sid::repeatBarlineDotSeparation) * 2.0
629 0 : + dotWidth * 2;
630 0 : break;
631 0 : case BarLineType::START_REPEAT:
632 : case BarLineType::END_REPEAT:
633 0 : w = ctx.style().styleAbsolute(Sid::endBarWidth)
634 0 : + ctx.style().styleAbsolute(Sid::barWidth)
635 0 : + ctx.style().styleAbsolute(Sid::endBarDistance)
636 0 : + ctx.style().styleAbsolute(Sid::repeatBarlineDotSeparation)
637 : + dotWidth;
638 0 : break;
639 0 : case BarLineType::END:
640 : case BarLineType::REVERSE_END:
641 0 : w = ctx.style().styleAbsolute(Sid::endBarWidth)
642 0 : + ctx.style().styleAbsolute(Sid::barWidth)
643 0 : + ctx.style().styleAbsolute(Sid::endBarDistance);
644 0 : break;
645 0 : case BarLineType::BROKEN:
646 0 : w = ctx.style().styleAbsolute(Sid::dashBarWidth);
647 0 : break;
648 0 : case BarLineType::NORMAL:
649 : case BarLineType::DOTTED:
650 0 : w = ctx.style().styleAbsolute(Sid::barWidth);
651 0 : break;
652 0 : case BarLineType::HEAVY:
653 0 : w = ctx.style().styleAbsolute(Sid::endBarWidth);
654 0 : break;
655 : }
656 0 : return w;
657 : };
658 :
659 0 : double w = layoutWidth(item, ctx) * ldata->mag();
660 0 : RectF bbox(0.0, ldata->y1, w, ldata->y2 - ldata->y1);
661 0 : ldata->setBbox(bbox);
662 0 : }
663 :
664 0 : void SingleLayout::layout(Bend* item, const Context&)
665 : {
666 0 : Bend::LayoutData* ldata = item->mutldata();
667 0 : double spatium = item->spatium();
668 0 : double lw = item->absoluteFromSpatium(item->lineWidth());
669 :
670 0 : ldata->noteWidth = 0.0;
671 0 : ldata->notePos = PointF();
672 :
673 0 : RectF bb;
674 :
675 0 : FontMetrics fm(item->font(spatium));
676 :
677 0 : size_t n = item->points().size();
678 0 : double x = ldata->noteWidth;
679 0 : double y = -spatium * .8;
680 : double x2, y2;
681 :
682 0 : double aw = spatium * .5;
683 0 : PolygonF arrowUp;
684 0 : arrowUp << PointF(0, 0) << PointF(aw * .5, aw) << PointF(-aw * .5, aw);
685 0 : PolygonF arrowDown;
686 0 : arrowDown << PointF(0, 0) << PointF(aw * .5, -aw) << PointF(-aw * .5, -aw);
687 :
688 0 : for (size_t pt = 0; pt < n; ++pt) {
689 0 : if (pt == (n - 1)) {
690 0 : break;
691 : }
692 0 : int pitch = item->points().at(pt).pitch;
693 0 : if (pt == 0 && pitch) {
694 0 : y2 = -ldata->notePos.y() - spatium * 2;
695 0 : x2 = x;
696 0 : bb.unite(RectF(x, y, x2 - x, y2 - y));
697 :
698 0 : bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
699 :
700 0 : int idx = (pitch + 12) / 25;
701 0 : const char* l = Bend::label[idx];
702 0 : bb.unite(fm.boundingRect(String::fromAscii(l)));
703 0 : y = y2;
704 : }
705 0 : if (pitch == item->points().at(pt + 1).pitch) {
706 0 : if (pt == (n - 2)) {
707 0 : break;
708 : }
709 0 : x2 = x + spatium;
710 0 : y2 = y;
711 0 : bb.unite(RectF(x, y, x2 - x, y2 - y));
712 0 : } else if (pitch < item->points().at(pt + 1).pitch) {
713 : // up
714 0 : x2 = x + spatium * .5;
715 0 : y2 = -ldata->notePos.y() - spatium * 2;
716 0 : double dx = x2 - x;
717 0 : double dy = y2 - y;
718 :
719 0 : PainterPath path;
720 0 : path.moveTo(x, y);
721 0 : path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
722 0 : bb.unite(path.boundingRect());
723 0 : bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
724 :
725 0 : int idx = (item->points().at(pt + 1).pitch + 12) / 25;
726 0 : const char* l = Bend::label[idx];
727 0 : bb.unite(fm.boundingRect(String::fromAscii(l)));
728 0 : } else {
729 : // down
730 0 : x2 = x + spatium * .5;
731 0 : y2 = y + spatium * 3;
732 0 : double dx = x2 - x;
733 0 : double dy = y2 - y;
734 :
735 0 : PainterPath path;
736 0 : path.moveTo(x, y);
737 0 : path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
738 0 : bb.unite(path.boundingRect());
739 :
740 0 : bb.unite(arrowDown.translated(x2, y2 - spatium * .2).boundingRect());
741 0 : }
742 0 : x = x2;
743 0 : y = y2;
744 : }
745 0 : bb.adjust(-lw, -lw, lw, lw);
746 0 : ldata->setBbox(bb);
747 0 : ldata->setPos(0.0, 0.0);
748 0 : }
749 :
750 0 : void SingleLayout::layout(Bracket* item, const Context& ctx)
751 : {
752 0 : Bracket::LayoutData* ldata = item->mutldata();
753 :
754 0 : ldata->bracketHeight = 3.5 * item->spatium() * 2;
755 0 : ldata->braceSymbol = item->braceSymbol();
756 :
757 0 : Shape shape;
758 :
759 0 : switch (item->bracketType()) {
760 0 : case BracketType::BRACE: {
761 0 : if (item->braceSymbol() == SymId::noSym) {
762 0 : ldata->braceSymbol = SymId::brace;
763 : }
764 0 : double h = ldata->bracketHeight;
765 0 : double w = item->symWidth(ldata->braceSymbol) * item->magx();
766 0 : ldata->setBbox(RectF(0, 0, w, h));
767 0 : ldata->shape.add(ldata->bbox());
768 0 : ldata->bracketWidth = w + ctx.style().styleAbsolute(Sid::akkoladeBarDistance);
769 : }
770 0 : break;
771 0 : case BracketType::NORMAL: {
772 0 : double spatium = item->spatium();
773 0 : double w = ctx.style().styleAbsolute(Sid::bracketWidth) * 0.5;
774 0 : double x = -w;
775 :
776 0 : double bd = spatium * 0.5;
777 0 : shape.add(RectF(x, -bd, w * 2, 2 * (item->ldata()->h2() + bd)));
778 0 : shape.add(item->symBbox(SymId::bracketTop).translated(PointF(-w, -bd)));
779 0 : shape.add(item->symBbox(SymId::bracketBottom).translated(PointF(-w, bd + 2 * item->ldata()->h2())));
780 :
781 0 : w += item->symWidth(SymId::bracketTop);
782 0 : double y = -item->symHeight(SymId::bracketTop) - bd;
783 0 : double h = (-y + item->ldata()->h2()) * 2;
784 0 : ldata->setBbox(x, y, w, h);
785 :
786 0 : ldata->bracketWidth = ctx.style().styleAbsolute(Sid::bracketWidth) + ctx.style().styleAbsolute(Sid::bracketDistance);
787 : }
788 0 : break;
789 0 : case BracketType::SQUARE:
790 : {
791 0 : double w = ctx.style().styleAbsolute(Sid::staffLineWidth) * .5;
792 0 : double x = -w;
793 0 : double y = -w;
794 0 : double h = (item->ldata()->h2() + w) * 2;
795 0 : w += (0.5 * item->spatium() + 3 * w);
796 0 : ldata->setBbox(x, y, w, h);
797 0 : shape.add(item->ldata()->bbox());
798 :
799 0 : ldata->bracketWidth = ctx.style().styleAbsolute(Sid::staffLineWidth) / 2 + 0.5 * item->spatium();
800 : }
801 0 : break;
802 0 : case BracketType::LINE: {
803 0 : double spatium = item->spatium();
804 0 : double w = 0.67 * ctx.style().styleAbsolute(Sid::bracketWidth) * 0.5;
805 0 : double x = -w;
806 0 : double bd = spatium * 0.25;
807 0 : double y = -bd;
808 0 : double h = (-y + item->ldata()->h2()) * 2;
809 0 : ldata->setBbox(x, y, w, h);
810 0 : shape.add(item->ldata()->bbox());
811 :
812 0 : ldata->bracketWidth = 0.67 * ctx.style().styleAbsolute(Sid::bracketWidth) + ctx.style().styleAbsolute(Sid::bracketDistance);
813 : }
814 0 : break;
815 0 : case BracketType::GROUP:
816 0 : layoutGroupBracket(item, ctx);
817 0 : break;
818 0 : case BracketType::NO_BRACKET:
819 0 : break;
820 : }
821 :
822 0 : ldata->shape = shape;
823 0 : }
824 :
825 0 : void SingleLayout::layoutGroupBracket(Bracket* item, const Context& ctx)
826 : {
827 0 : Bracket::LayoutData* ldata = item->mutldata();
828 0 : ldata->bracketHeight = 12 * item->spatium();
829 0 : double w = ctx.style().styleAbsolute(Sid::staffLineWidth) * 0.5;
830 0 : double x = 0.0;
831 0 : double y = -w;
832 0 : double h = (ldata->bracketHeight * 0.5 + w) * 2;
833 0 : double width = item->spatium();
834 0 : ldata->setBbox(RectF(x, y, width, h));
835 0 : ldata->shape.add(ldata->bbox());
836 :
837 0 : ldata->bracketWidth = width;
838 :
839 0 : if (!item->text()) {
840 0 : const_cast<Bracket*>(item)->setText(new Text(const_cast<Bracket*>(item)));
841 : }
842 :
843 0 : Text* text = item->text();
844 0 : text->setAlign(Align(AlignH::HCENTER, AlignV::VCENTER));
845 0 : text->setPosition(AlignH::HCENTER);
846 0 : text->setTextAngle(-90);
847 0 : text->setXmlText(item->bracketItem()->longName());
848 0 : layout(text, ctx);
849 0 : text->mutldata()->setPos(0.0, item->ldata()->bbox().height() / 2);
850 :
851 0 : double textPadding = 0.5 * item->spatium();
852 0 : RectF mask = text->ldata()->bbox().translated(text->pos()).padded(textPadding);
853 0 : ldata->setMask(mask);
854 0 : }
855 :
856 0 : void SingleLayout::layout(Breath* item, const Context&)
857 : {
858 0 : item->setbbox(item->symBbox(item->symId()));
859 0 : }
860 :
861 0 : void SingleLayout::layout(Capo* item, const Context& ctx)
862 : {
863 0 : layoutTextBase(item, ctx, item->mutldata());
864 0 : }
865 :
866 0 : void SingleLayout::layout(Chord* item, const Context& ctx)
867 : {
868 0 : LayoutContext tctx(ctx.dontUseScore());
869 0 : ChordLayout::computeUp(item, tctx);
870 0 : ChordLayout::layout(item, tctx);
871 0 : ChordLayout::layoutStem(item, tctx);
872 0 : ChordLayout::layoutLedgerLines({ item }, tctx);
873 0 : }
874 :
875 0 : void SingleLayout::layout(ChordLine* item, const Context& ctx)
876 : {
877 0 : ChordLine::LayoutData* ldata = item->mutldata();
878 0 : ldata->setMag(1.0);
879 0 : TLayout::layoutChordLinePath(item, ldata, item->spatium(), item->chordMag());
880 :
881 0 : ldata->setPos(0.0, 0.0);
882 :
883 0 : if (!item->isWavy()) {
884 0 : RectF r = ldata->path.boundingRect();
885 0 : int x1 = 0, y1 = 0, width = 0, height = 0;
886 :
887 0 : x1 = r.x();
888 0 : y1 = r.y();
889 0 : width = r.width();
890 0 : height = r.height();
891 0 : ldata->setBbox(x1, y1, width, height);
892 : } else {
893 0 : RectF r = ctx.engravingFont()->bbox(item->waveSym(), item->magS());
894 :
895 0 : ldata->setBbox(r);
896 : }
897 0 : }
898 :
899 0 : void SingleLayout::layout(Clef* item, const Context& ctx)
900 : {
901 0 : Clef::LayoutData* ldata = item->mutldata();
902 0 : constexpr int lines = 5;
903 0 : constexpr Spatium lineDist = 1.0_sp;
904 0 : double spatium = ctx.style().spatium();
905 0 : Spatium yoff = 0.0_sp;
906 :
907 0 : if (item->clefType() != ClefType::INVALID && item->clefType() != ClefType::MAX) {
908 0 : ldata->symId = ClefInfo::symId(item->clefType());
909 0 : yoff = lineDist * (5 - ClefInfo::line(item->clefType()));
910 : } else {
911 0 : ldata->symId = SymId::noSym;
912 : }
913 :
914 0 : switch (item->clefType()) {
915 0 : case ClefType::C_19C: // 19th C clef is like a G clef
916 0 : yoff = lineDist * 1.5;
917 0 : break;
918 0 : case ClefType::TAB: // TAB clef
919 : case ClefType::TAB4: // TAB clef 4 strings
920 : case ClefType::TAB_SERIF: // TAB clef alternate style
921 : case ClefType::TAB4_SERIF: // TAB clef alternate style
922 0 : yoff = lineDist * (lines - 1) * 0.5;
923 0 : break;
924 0 : case ClefType::PERC: // percussion clefs
925 : case ClefType::PERC2:
926 0 : yoff = lineDist * (lines - 1) * 0.5;
927 0 : break;
928 0 : default:
929 0 : break;
930 : }
931 :
932 0 : ldata->setPos(0.0, yoff.toAbsolute(spatium));
933 :
934 0 : RectF bbox = item->symBbox(ldata->symId);
935 0 : ldata->setBbox(bbox);
936 0 : }
937 :
938 0 : void SingleLayout::layout(Expression* item, const Context& ctx)
939 : {
940 0 : layoutTextBase(item, ctx, item->mutldata());
941 0 : }
942 :
943 0 : void SingleLayout::layout(Fermata* item, const Context&)
944 : {
945 0 : item->setPos(PointF());
946 0 : item->setOffset(PointF());
947 0 : RectF b(item->symBbox(item->symId()));
948 0 : item->setbbox(b.translated(-0.5 * b.width(), 0.0));
949 0 : }
950 :
951 0 : void SingleLayout::layout(Fingering* item, const Context& ctx)
952 : {
953 0 : layoutTextBase(item, ctx, item->mutldata());
954 0 : }
955 :
956 0 : void SingleLayout::layout(FretDiagram* item, const Context& ctx)
957 : {
958 0 : FretDiagram::LayoutData* ldata = item->mutldata();
959 0 : double spatium = item->spatium();
960 0 : ldata->stringLineWidth = (spatium * 0.08);
961 0 : ldata->nutLineWidth = ((item->fretOffset() || !item->showNut()) ? ldata->stringLineWidth : spatium * 0.2);
962 0 : ldata->stringDist = (ctx.style().styleAbsolute(Sid::fretStringSpacing));
963 0 : ldata->fretDist = (ctx.style().styleAbsolute(Sid::fretFretSpacing));
964 0 : ldata->markerSize = (ldata->stringDist * 0.8);
965 :
966 0 : double w = ldata->stringDist * (item->strings() - 1) + ldata->markerSize;
967 0 : double h = (item->frets() + 1) * ldata->fretDist + ldata->markerSize;
968 0 : double y = -(ldata->markerSize * 0.5 + ldata->fretDist);
969 0 : double x = -(ldata->markerSize * 0.5);
970 :
971 : // Allocate space for fret offset number
972 0 : if (item->fretOffset() > 0) {
973 0 : Font scaledFont(item->fretNumFont());
974 0 : scaledFont.setPointSizeF(item->fretNumFont().pointSizeF() * item->userMag());
975 :
976 0 : FontMetrics fm2(scaledFont);
977 0 : double numw = fm2.width(String::number(item->fretOffset() + 1));
978 0 : double xdiff = numw + ldata->stringDist * .4;
979 0 : w += xdiff;
980 0 : x += (item->numPos() == 0) == (item->orientation() == engraving::Orientation::VERTICAL) ? -xdiff : 0;
981 0 : }
982 :
983 0 : if (item->orientation() == engraving::Orientation::HORIZONTAL) {
984 0 : std::swap(w, h);
985 0 : std::swap(x, y);
986 : }
987 :
988 0 : ldata->setBbox(x, y, w, h);
989 0 : }
990 :
991 0 : void SingleLayout::layout(FSymbol* item, const Context&)
992 : {
993 0 : item->setbbox(FontMetrics::boundingRect(item->font(), item->toString()));
994 0 : item->setOffset(0.0, 0.0);
995 0 : item->setPos(0.0, 0.0);
996 0 : }
997 :
998 0 : void SingleLayout::layout(Dynamic* item, const Context& ctx)
999 : {
1000 0 : layoutTextBase(item, ctx, item->mutldata());
1001 0 : }
1002 :
1003 0 : void SingleLayout::layout(Glissando* item, const Context& ctx)
1004 : {
1005 0 : double spatium = item->spatium();
1006 :
1007 0 : if (item->spannerSegments().empty()) {
1008 0 : item->add(item->createLineSegment());
1009 : }
1010 0 : LineSegment* s = item->frontSegment();
1011 0 : s->setPos(PointF(-spatium * Glissando::GLISS_PALETTE_WIDTH / 2, spatium * Glissando::GLISS_PALETTE_HEIGHT / 2));
1012 0 : s->setPos2(PointF(spatium * Glissando::GLISS_PALETTE_WIDTH, -spatium * Glissando::GLISS_PALETTE_HEIGHT));
1013 0 : layout(static_cast<GlissandoSegment*>(s), ctx);
1014 0 : }
1015 :
1016 0 : void SingleLayout::layout(GlissandoSegment* item, const Context&)
1017 : {
1018 0 : if (item->pos2().x() <= 0) {
1019 0 : item->setbbox(RectF());
1020 0 : return;
1021 : }
1022 :
1023 0 : RectF r = RectF(0.0, 0.0, item->pos2().x(), item->pos2().y()).normalized();
1024 0 : double lw = item->absoluteFromSpatium(item->lineWidth()) * .5;
1025 0 : item->setbbox(r.adjusted(-lw, -lw, lw, lw));
1026 : }
1027 :
1028 0 : void SingleLayout::layout(GradualTempoChange* item, const Context& ctx)
1029 : {
1030 0 : layoutLine(item, ctx);
1031 0 : }
1032 :
1033 0 : void SingleLayout::layout(GradualTempoChangeSegment* item, const Context& ctx)
1034 : {
1035 0 : layoutTextLineBaseSegment(item, ctx);
1036 0 : item->setOffset(PointF());
1037 0 : }
1038 :
1039 0 : void SingleLayout::layout(GuitarBend*, const Context&)
1040 : {
1041 0 : NOT_IMPLEMENTED;
1042 : //! NOTE: Bends can be removed from disallowed elements in NotationInteraction::isOutgoingDragElementAllowed once this has been implemented
1043 0 : }
1044 :
1045 0 : void SingleLayout::layout(GuitarBendSegment*, const Context&)
1046 : {
1047 0 : NOT_IMPLEMENTED;
1048 0 : }
1049 :
1050 0 : void SingleLayout::layout(Hairpin* item, const Context& ctx)
1051 : {
1052 0 : item->setPos(0.0, 0.0);
1053 0 : layoutLine(item, ctx);
1054 0 : }
1055 :
1056 0 : void SingleLayout::layout(HammerOnPullOff* item, const Context& ctx)
1057 : {
1058 0 : double spatium = item->spatium();
1059 0 : HammerOnPullOffSegment* s = nullptr;
1060 0 : if (item->spannerSegments().empty()) {
1061 0 : s = new HammerOnPullOffSegment(item);
1062 0 : s->setTrack(item->track());
1063 0 : item->add(s);
1064 : } else {
1065 0 : s = toHammerOnPullOffSegment(item->frontSegment());
1066 : }
1067 :
1068 0 : s->setSpannerSegmentType(SpannerSegmentType::SINGLE);
1069 :
1070 0 : s->setPos(PointF());
1071 0 : s->ups(Grip::START).p = PointF(0, 0);
1072 0 : s->ups(Grip::END).p = PointF(spatium * 6, 0);
1073 0 : s->setExtraHeight(0.0);
1074 :
1075 0 : SlurTieLayout::computeBezier(s);
1076 :
1077 0 : layout(s, ctx);
1078 :
1079 0 : item->setbbox(s->ldata()->bbox());
1080 0 : }
1081 :
1082 0 : void SingleLayout::layout(HammerOnPullOffSegment* item, const Context& ctx)
1083 : {
1084 0 : const std::vector<HammerOnPullOffText*>& hopoTexts = item->hopoText();
1085 0 : if (item->hopoText().empty()) {
1086 0 : item->addHopoText(new HammerOnPullOffText(item));
1087 : }
1088 :
1089 0 : HammerOnPullOffText* hopoText = hopoTexts.front();
1090 0 : hopoText->setXmlText("H/P");
1091 :
1092 0 : Align align;
1093 0 : align.vertical = AlignV::BASELINE;
1094 0 : align.horizontal = AlignH::HCENTER;
1095 0 : hopoText->setAlign(align);
1096 0 : hopoText->setPosition(AlignH::HCENTER);
1097 0 : layoutTextBase(hopoText, ctx, hopoText->mutldata());
1098 :
1099 0 : RectF bbox = item->ldata()->bbox();
1100 0 : double x = 0.5 * (bbox.left() + bbox.right());
1101 0 : double y = bbox.top() - 0.5 * item->spatium();
1102 0 : hopoText->mutldata()->setPos(x, y);
1103 :
1104 0 : Shape itemShape = item->mutldata()->shape();
1105 0 : itemShape.add(hopoText->shape().translated(hopoText->pos()));
1106 0 : item->mutldata()->setShape(itemShape);
1107 0 : }
1108 :
1109 0 : void SingleLayout::layout(HairpinSegment* item, const Context& ctx)
1110 : {
1111 0 : const double spatium = item->spatium();
1112 0 : HairpinSegment::LayoutData* ldata = item->mutldata();
1113 :
1114 0 : HairpinType type = item->hairpin()->hairpinType();
1115 0 : if (item->hairpin()->isLineType()) {
1116 0 : item->setTwoLines(false);
1117 0 : layoutTextLineBaseSegment(item, ctx);
1118 0 : item->setDrawCircledTip(false);
1119 0 : item->setCircledTipRadius(0.0);
1120 : } else {
1121 0 : item->setTwoLines(true);
1122 :
1123 0 : item->hairpin()->setBeginTextAlign({ AlignH::LEFT, AlignV::VCENTER });
1124 0 : item->hairpin()->setEndTextAlign({ AlignH::RIGHT, AlignV::VCENTER });
1125 :
1126 0 : double x1 = 0.0;
1127 0 : layoutTextLineBaseSegment(item, ctx);
1128 0 : if (!item->text()->empty()) {
1129 0 : x1 = item->text()->width() + spatium * .5;
1130 : }
1131 :
1132 0 : Transform t;
1133 0 : double h1 = item->hairpin()->hairpinHeight().val() * spatium * .5;
1134 0 : double h2 = item->hairpin()->hairpinContHeight().val() * spatium * .5;
1135 :
1136 0 : double x = item->pos2().x();
1137 0 : if (!item->endText()->empty()) {
1138 0 : x -= (item->endText()->width() + spatium * .5); // 0.5 spatium distance
1139 : }
1140 0 : if (x < spatium) { // minimum size of hairpin
1141 0 : x = spatium;
1142 : }
1143 0 : double y = item->pos2().y();
1144 0 : double len = sqrt(x * x + y * y);
1145 0 : t.rotateRadians(asin(y / len));
1146 :
1147 0 : item->setDrawCircledTip(item->hairpin()->hairpinCircledTip());
1148 0 : item->setCircledTipRadius(item->drawCircledTip() ? 0.6 * spatium * .5 : 0.0);
1149 :
1150 0 : LineF l1, l2;
1151 :
1152 0 : switch (type) {
1153 0 : case HairpinType::CRESC_HAIRPIN: {
1154 0 : switch (item->spannerSegmentType()) {
1155 0 : case SpannerSegmentType::SINGLE:
1156 : case SpannerSegmentType::BEGIN: {
1157 0 : l1.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, h1);
1158 0 : l2.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, -h1);
1159 0 : PointF circledTip;
1160 0 : circledTip.setX(x1 + item->circledTipRadius());
1161 0 : circledTip.setY(0.0);
1162 0 : item->setCircledTip(circledTip);
1163 0 : } break;
1164 :
1165 0 : case SpannerSegmentType::MIDDLE:
1166 : case SpannerSegmentType::END:
1167 0 : item->setDrawCircledTip(false);
1168 0 : l1.setLine(x1, h2, len, h1);
1169 0 : l2.setLine(x1, -h2, len, -h1);
1170 0 : break;
1171 : }
1172 : }
1173 0 : break;
1174 0 : case HairpinType::DIM_HAIRPIN: {
1175 0 : switch (item->spannerSegmentType()) {
1176 0 : case SpannerSegmentType::SINGLE:
1177 : case SpannerSegmentType::END: {
1178 0 : l1.setLine(x1, h1, len - item->circledTipRadius() * 2, 0.0);
1179 0 : l2.setLine(x1, -h1, len - item->circledTipRadius() * 2, 0.0);
1180 0 : PointF circledTip;
1181 0 : circledTip.setX(len - item->circledTipRadius());
1182 0 : circledTip.setY(0.0);
1183 0 : item->setCircledTip(circledTip);
1184 0 : } break;
1185 0 : case SpannerSegmentType::BEGIN:
1186 : case SpannerSegmentType::MIDDLE:
1187 0 : item->setDrawCircledTip(false);
1188 0 : l1.setLine(x1, h1, len, +h2);
1189 0 : l2.setLine(x1, -h1, len, -h2);
1190 0 : break;
1191 : }
1192 : }
1193 0 : break;
1194 0 : default:
1195 0 : break;
1196 : }
1197 :
1198 : // Do Coord rotation
1199 0 : l1 = t.map(l1);
1200 0 : l2 = t.map(l2);
1201 0 : if (item->drawCircledTip()) {
1202 0 : item->setCircledTip(t.map(item->circledTip()));
1203 : }
1204 :
1205 0 : ldata->points[0] = l1.p1();
1206 0 : ldata->points[1] = l1.p2();
1207 0 : ldata->points[2] = l2.p1();
1208 0 : ldata->points[3] = l2.p2();
1209 0 : ldata->npoints = 4;
1210 :
1211 0 : RectF r = RectF(l1.p1(), l1.p2()).normalized().united(RectF(l2.p1(), l2.p2()).normalized());
1212 0 : if (!item->text()->empty()) {
1213 0 : r.unite(item->text()->ldata()->bbox());
1214 : }
1215 0 : if (!item->endText()->empty()) {
1216 0 : r.unite(item->endText()->ldata()->bbox().translated(x + item->endText()->ldata()->bbox().width(), 0.0));
1217 : }
1218 0 : double w = item->absoluteFromSpatium(ctx.style().styleS(Sid::hairpinLineWidth));
1219 0 : item->setbbox(r.adjusted(-w * .5, -w * .5, w, w));
1220 : }
1221 :
1222 0 : item->setPos(PointF());
1223 0 : item->setOffset(PointF());
1224 0 : }
1225 :
1226 0 : void SingleLayout::layout(HarpPedalDiagram* item, const Context& ctx)
1227 : {
1228 0 : item->updateDiagramText();
1229 0 : layoutTextBase(item, ctx, item->mutldata());
1230 0 : }
1231 :
1232 0 : void SingleLayout::layout(Image* item, const Context&)
1233 : {
1234 0 : item->init();
1235 :
1236 0 : SizeF imageSize = item->size();
1237 :
1238 0 : Image::LayoutData* ldata = item->mutldata();
1239 0 : ldata->setPos(PointF());
1240 0 : ldata->setBbox(RectF(PointF(), item->size2pixel(imageSize)));
1241 0 : }
1242 :
1243 0 : void SingleLayout::layout(InstrumentChange* item, const Context& ctx)
1244 : {
1245 0 : layoutTextBase(item, ctx, item->mutldata());
1246 0 : }
1247 :
1248 0 : void SingleLayout::layout(Jump* item, const Context& ctx)
1249 : {
1250 0 : layoutTextBase(item, ctx, item->mutldata());
1251 0 : }
1252 :
1253 0 : void SingleLayout::layout(KeySig* item, const Context& ctx)
1254 : {
1255 0 : KeySig::LayoutData* ldata = item->mutldata();
1256 0 : double spatium = item->spatium();
1257 0 : double step = spatium * 0.5;
1258 :
1259 0 : ldata->setBbox(RectF());
1260 0 : ldata->keySymbols.clear();
1261 :
1262 : // determine current clef for this staff
1263 0 : ClefType clef = ClefType::G;
1264 :
1265 0 : int key = int(item->key());
1266 :
1267 0 : if (item->isCustom() && !item->isAtonal()) {
1268 0 : Spatium accidentalGap = ctx.style().styleS(Sid::keysigAccidentalDistance);
1269 : // add standard key accidentals first, if necessary
1270 0 : for (int i = 1; i <= std::abs(key) && std::abs(key) <= 7; ++i) {
1271 0 : bool drop = false;
1272 0 : for (const CustDef& cd: item->customKeyDefs()) {
1273 0 : int degree = item->degInKey(cd.degree);
1274 : // if custom keysig accidental takes place, don't create tonal accidental
1275 0 : if ((degree * 2 + 2) % 7 == (key < 0 ? 8 - i : i) % 7) {
1276 0 : drop = true;
1277 0 : break;
1278 : }
1279 : }
1280 0 : if (!drop) {
1281 0 : KeySym ks;
1282 0 : int lineIndexOffset = key > 0 ? -1 : 6;
1283 0 : ks.sym = key > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
1284 0 : ks.line = ClefInfo::lines(clef)[lineIndexOffset + i];
1285 0 : if (ldata->keySymbols.size() > 0) {
1286 0 : KeySym& previous = ldata->keySymbols.back();
1287 0 : Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
1288 0 : ks.xPos = previous.xPos + previousWidth + accidentalGap;
1289 : } else {
1290 0 : ks.xPos = 0_sp;
1291 : }
1292 : // TODO octave metters?
1293 0 : ldata->keySymbols.push_back(ks);
1294 : }
1295 : }
1296 0 : for (const CustDef& cd : item->customKeyDefs()) {
1297 0 : SymId sym = item->symInKey(cd.sym, cd.degree);
1298 0 : int degree = item->degInKey(cd.degree);
1299 0 : bool flat = std::string(SymNames::nameForSymId(sym).ascii()).find("Flat") != std::string::npos;
1300 0 : int accIdx = (degree * 2 + 1) % 7; // C D E F ... index to F C G D index
1301 0 : accIdx = flat ? 13 - accIdx : accIdx;
1302 0 : int line = ClefInfo::lines(clef)[accIdx] + cd.octAlt * 7;
1303 0 : Spatium xpos = cd.xAlt;
1304 0 : if (ldata->keySymbols.size() > 0) {
1305 0 : KeySym& previous = ldata->keySymbols.back();
1306 0 : Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
1307 0 : xpos += previous.xPos + previousWidth + accidentalGap;
1308 : }
1309 : // if translated symbol if out of range, add key accidental followed by untranslated symbol
1310 0 : if (sym == SymId::noSym) {
1311 0 : KeySym ks;
1312 0 : ks.line = line;
1313 0 : ks.xPos = xpos;
1314 : // for quadruple sharp use two double sharps
1315 0 : if (cd.sym == SymId::accidentalTripleSharp) {
1316 0 : ks.sym = SymId::accidentalDoubleSharp;
1317 0 : sym = SymId::accidentalDoubleSharp;
1318 : } else {
1319 0 : ks.sym = key > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
1320 0 : sym = cd.sym;
1321 : }
1322 0 : ldata->keySymbols.push_back(ks);
1323 0 : xpos += Spatium(key < 0 ? 0.7 : 1); // flats closer
1324 : }
1325 : // create symbol; natural only if is user defined
1326 0 : if (sym != SymId::accidentalNatural || sym == cd.sym) {
1327 0 : KeySym ks;
1328 0 : ks.sym = sym;
1329 0 : ks.line = line;
1330 0 : ks.xPos = xpos;
1331 0 : ldata->keySymbols.push_back(ks);
1332 : }
1333 : }
1334 : } else {
1335 0 : if (std::abs(key) <= 7) {
1336 0 : const signed char* lines = ClefInfo::lines(clef);
1337 0 : SymId sym = key > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
1338 0 : Spatium accidentalGap = ctx.style().styleS(Sid::keysigAccidentalDistance);
1339 0 : Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(sym), spatium);
1340 0 : int lineIndexOffset = key > 0 ? 0 : 7;
1341 0 : for (int i = 0; i < std::abs(key); ++i) {
1342 0 : int line = lines[lineIndexOffset + i];
1343 0 : KeySym ks;
1344 0 : ks.sym = sym;
1345 0 : Spatium x = 0.0_sp;
1346 0 : if (ldata->keySymbols.size() > 0) {
1347 0 : const KeySym& previous = ldata->keySymbols.back();
1348 0 : x = previous.xPos + previousWidth + accidentalGap;
1349 0 : bool isAscending = line < previous.line;
1350 0 : SmuflAnchorId currentCutout = isAscending ? SmuflAnchorId::cutOutSW : SmuflAnchorId::cutOutNW;
1351 0 : SmuflAnchorId previousCutout = isAscending ? SmuflAnchorId::cutOutNE : SmuflAnchorId::cutOutSE;
1352 0 : PointF cutout = item->symSmuflAnchor(sym, currentCutout);
1353 0 : double currentCutoutY = line * step + cutout.y();
1354 0 : double previousCutoutY = previous.line * step + item->symSmuflAnchor(previous.sym, previousCutout).y();
1355 0 : if ((isAscending && currentCutoutY < previousCutoutY) || (!isAscending && currentCutoutY > previousCutoutY)) {
1356 0 : x -= Spatium::fromAbsolute(cutout.x(), spatium);
1357 : }
1358 : }
1359 0 : ks.xPos = x;
1360 0 : ks.line = line;
1361 0 : ldata->keySymbols.push_back(ks);
1362 : }
1363 : } else {
1364 0 : LOGD() << "illegal key:" << key;
1365 : }
1366 : }
1367 :
1368 : // compute bbox
1369 0 : for (const KeySym& ks : ldata->keySymbols) {
1370 0 : double x = ks.xPos.toAbsolute(spatium);
1371 0 : double y = ks.line * step;
1372 0 : ldata->addBbox(item->symBbox(ks.sym).translated(x, y));
1373 : }
1374 0 : }
1375 :
1376 0 : void SingleLayout::layout(LayoutBreak* item, const Context&)
1377 : {
1378 0 : FontMetrics metrics(item->font());
1379 0 : RectF bbox = metrics.boundingRect(item->iconCode());
1380 0 : item->mutldata()->setShape(Shape(bbox, item));
1381 0 : }
1382 :
1383 0 : void SingleLayout::layout(LetRing* item, const Context& ctx)
1384 : {
1385 0 : layoutLine(item, ctx);
1386 0 : }
1387 :
1388 0 : void SingleLayout::layout(LetRingSegment* item, const Context& ctx)
1389 : {
1390 0 : layoutTextLineBaseSegment(item, ctx);
1391 0 : }
1392 :
1393 0 : void SingleLayout::layout(Lyrics* item, const Context& ctx)
1394 : {
1395 0 : layoutTextBase(toTextBase(item), ctx, item->mutldata());
1396 0 : }
1397 :
1398 0 : void SingleLayout::layout(NoteHead* item, const Context& ctx)
1399 : {
1400 0 : layout(static_cast<Symbol*>(item), ctx);
1401 0 : }
1402 :
1403 0 : void SingleLayout::layout(NoteLine*, const Context&)
1404 : {
1405 0 : NOT_IMPLEMENTED;
1406 : //! NOTE: NoteLines can be removed from disallowed elements in NotationInteraction::isOutgoingDragElementAllowed once this has been implemented
1407 0 : }
1408 :
1409 0 : void SingleLayout::layout(Marker* item, const Context& ctx)
1410 : {
1411 0 : layoutTextBase(item, ctx, item->mutldata());
1412 0 : }
1413 :
1414 0 : void SingleLayout::layout(MeasureNumber* item, const Context& ctx)
1415 : {
1416 0 : item->setPos(PointF());
1417 0 : item->setOffset(PointF());
1418 :
1419 0 : layout1TextBase(item, ctx, item->mutldata());
1420 0 : }
1421 :
1422 0 : void SingleLayout::layout(MeasureRepeat* item, const Context& ctx)
1423 : {
1424 0 : MeasureRepeat::LayoutData* ldata = item->mutldata();
1425 0 : switch (item->numMeasures()) {
1426 0 : case 1:
1427 : {
1428 0 : ldata->setSymId(SymId::repeat1Bar);
1429 0 : if (ctx.style().styleB(Sid::oneMeasureRepeatShow1)) {
1430 0 : ldata->setNumberSym(1);
1431 : } else {
1432 0 : ldata->clearNumberSym();
1433 : }
1434 0 : break;
1435 : }
1436 0 : case 2:
1437 0 : ldata->setSymId(SymId::repeat2Bars);
1438 0 : ldata->setNumberSym(item->numMeasures());
1439 0 : break;
1440 0 : case 4:
1441 0 : ldata->setSymId(SymId::repeat4Bars);
1442 0 : ldata->setNumberSym(item->numMeasures());
1443 0 : break;
1444 0 : default:
1445 0 : ldata->setSymId(SymId::noSym); // should never happen
1446 0 : ldata->clearNumberSym();
1447 0 : break;
1448 : }
1449 :
1450 0 : RectF bbox = item->symBbox(ldata->symId);
1451 0 : ldata->setBbox(bbox);
1452 0 : }
1453 :
1454 0 : void SingleLayout::layout(Ornament* item, const Context& ctx)
1455 : {
1456 0 : layout(toArticulation(item), ctx);
1457 0 : }
1458 :
1459 0 : void SingleLayout::layout(Ottava* item, const Context& ctx)
1460 : {
1461 0 : layoutLine(item, ctx);
1462 0 : }
1463 :
1464 0 : void SingleLayout::layout(OttavaSegment* item, const Context& ctx)
1465 : {
1466 0 : layoutTextLineBaseSegment(item, ctx);
1467 0 : item->setOffset(PointF());
1468 0 : }
1469 :
1470 0 : void SingleLayout::layout(PalmMute* item, const Context& ctx)
1471 : {
1472 0 : layoutLine(item, ctx);
1473 0 : }
1474 :
1475 0 : void SingleLayout::layout(PalmMuteSegment* item, const Context& ctx)
1476 : {
1477 0 : layoutTextLineBaseSegment(item, ctx);
1478 0 : }
1479 :
1480 0 : void SingleLayout::layout(Pedal* item, const Context& ctx)
1481 : {
1482 0 : layoutLine(item, ctx);
1483 0 : }
1484 :
1485 0 : void SingleLayout::layout(PedalSegment* item, const Context& ctx)
1486 : {
1487 0 : layoutTextLineBaseSegment(item, ctx);
1488 0 : item->setOffset(PointF());
1489 0 : }
1490 :
1491 0 : void SingleLayout::layout(PlayCountText* item, const Context& ctx)
1492 : {
1493 0 : layoutTextBase(item, ctx, item->mutldata());
1494 0 : }
1495 :
1496 0 : void SingleLayout::layout(PlayTechAnnotation* item, const Context& ctx)
1497 : {
1498 0 : layoutTextBase(item, ctx, item->mutldata());
1499 0 : }
1500 :
1501 0 : void SingleLayout::layout(RehearsalMark* item, const Context& ctx)
1502 : {
1503 0 : layoutTextBase(item, ctx, item->mutldata());
1504 0 : }
1505 :
1506 0 : void SingleLayout::layout(Slur* item, const Context&)
1507 : {
1508 0 : double spatium = item->spatium();
1509 0 : SlurSegment* s = nullptr;
1510 0 : if (item->spannerSegments().empty()) {
1511 0 : s = new SlurSegment(item);
1512 0 : s->setTrack(item->track());
1513 0 : item->add(s);
1514 : } else {
1515 0 : s = item->frontSegment();
1516 : }
1517 :
1518 0 : s->setSpannerSegmentType(SpannerSegmentType::SINGLE);
1519 :
1520 0 : s->setPos(PointF());
1521 0 : s->ups(Grip::START).p = PointF(0, 0);
1522 0 : s->ups(Grip::END).p = PointF(spatium * 6, 0);
1523 0 : s->setExtraHeight(0.0);
1524 :
1525 0 : SlurTieLayout::computeBezier(s);
1526 :
1527 0 : item->setbbox(s->ldata()->bbox());
1528 0 : }
1529 :
1530 0 : void SingleLayout::layout(Spacer* item, const Context&)
1531 : {
1532 0 : Spacer::LayoutData* ldata = item->mutldata();
1533 :
1534 0 : double spatium = item->spatium();
1535 :
1536 0 : PainterPath path = PainterPath();
1537 0 : double w = spatium;
1538 0 : double b = w * .5;
1539 0 : double h = item->ownershipParent() ? item->absoluteGap() : item->absoluteFromSpatium(std::min(item->gap(), 4.0_sp)); // limit length for palette
1540 :
1541 0 : switch (item->spacerType()) {
1542 0 : case SpacerType::DOWN:
1543 0 : path.lineTo(w, 0.0);
1544 0 : path.moveTo(b, 0.0);
1545 0 : path.lineTo(b, h);
1546 0 : path.lineTo(0.0, h - b);
1547 0 : path.moveTo(b, h);
1548 0 : path.lineTo(w, h - b);
1549 0 : break;
1550 0 : case SpacerType::UP:
1551 0 : path.moveTo(b, 0.0);
1552 0 : path.lineTo(0.0, b);
1553 0 : path.moveTo(b, 0.0);
1554 0 : path.lineTo(w, b);
1555 0 : path.moveTo(b, 0.0);
1556 0 : path.lineTo(b, h);
1557 0 : path.moveTo(0.0, h);
1558 0 : path.lineTo(w, h);
1559 0 : break;
1560 0 : case SpacerType::FIXED:
1561 0 : path.lineTo(w, 0.0);
1562 0 : path.moveTo(b, 0.0);
1563 0 : path.lineTo(b, h);
1564 0 : path.moveTo(0.0, h);
1565 0 : path.lineTo(w, h);
1566 0 : break;
1567 : }
1568 0 : ldata->path = path;
1569 0 : double lw = spatium * 0.4;
1570 0 : RectF bb(0, 0, w, h);
1571 0 : bb.adjust(-lw, -lw, lw, lw);
1572 0 : ldata->setBbox(bb);
1573 :
1574 0 : item->setZ(0.0);
1575 0 : }
1576 :
1577 0 : void SingleLayout::layout(StaffText* item, const Context& ctx)
1578 : {
1579 0 : layoutTextBase(item, ctx, item->mutldata());
1580 :
1581 0 : if (item->hasSoundFlag()) {
1582 0 : RectF bbox = item->ldata()->bbox();
1583 0 : double iconHeight = bbox.height();
1584 0 : RectF iconBBox = RectF(bbox.x(), bbox.y(), iconHeight, iconHeight);
1585 0 : item->soundFlag()->mutldata()->setBbox(iconBBox);
1586 :
1587 0 : double xMove = iconBBox.width() + iconBBox.width() / 2.0;
1588 0 : bbox.setWidth(bbox.width() + xMove);
1589 0 : item->setbbox(bbox);
1590 :
1591 0 : layout(item->soundFlag(), ctx);
1592 :
1593 0 : for (TextBlock& block : item->mutldata()->blocks) {
1594 0 : auto& fragments = block.fragments();
1595 0 : for (std::list<TextFragment>::iterator it = fragments.begin(); it != fragments.end(); ++it) {
1596 0 : it->pos.setX(it->pos.x() + xMove);
1597 : }
1598 : }
1599 : }
1600 0 : }
1601 :
1602 0 : void SingleLayout::layout(StaveSharingLabel* item, const Context& ctx)
1603 : {
1604 0 : layoutTextBase(item, ctx, item->mutldata());
1605 0 : }
1606 :
1607 0 : void SingleLayout::layout(StaffTypeChange* item, const Context& ctx)
1608 : {
1609 0 : double spatium = ctx.style().spatium();
1610 0 : item->setbbox(RectF(-item->lw() * .5, -item->lw() * .5, spatium * 2.5 + item->lw(), spatium * 2.5 + item->lw()));
1611 0 : item->setPos(0.0, 0.0);
1612 0 : }
1613 :
1614 0 : void SingleLayout::layout(StringTunings* item, const Context& ctx)
1615 : {
1616 0 : layoutTextBase(item, ctx, item->mutldata());
1617 :
1618 0 : double spatium = item->spatium();
1619 0 : for (TextBlock& block : item->mutldata()->blocks) {
1620 0 : for (TextFragment& fragment : block.fragments()) {
1621 0 : Font font = fragment.font(item);
1622 0 : if (font.type() != Font::Type::MusicSymbol) {
1623 : // HACK: the music symbol doesn't have a good baseline
1624 : // to go with text so we correct text here
1625 0 : const double baselineAdjustment = 0.35 * spatium * item->symbolScale();
1626 0 : fragment.pos.setY(fragment.pos.y() - baselineAdjustment);
1627 : }
1628 0 : }
1629 : }
1630 0 : }
1631 :
1632 0 : void SingleLayout::layout(Symbol* item, const Context&)
1633 : {
1634 0 : item->setbbox(item->scoreFont() ? item->scoreFont()->bbox(item->sym(), item->magS()) : item->symBbox(item->sym()));
1635 0 : item->setOffset(0.0, 0.0);
1636 0 : item->setPos(0.0, 0.0);
1637 0 : }
1638 :
1639 0 : void SingleLayout::layout(SystemText* item, const Context& ctx)
1640 : {
1641 0 : layoutTextBase(item, ctx, item->mutldata());
1642 0 : }
1643 :
1644 0 : void SingleLayout::layout(Tapping* item, const Context& ctx)
1645 : {
1646 0 : TappingText* text = item->text();
1647 :
1648 0 : if (!text) {
1649 0 : text = new TappingText(item);
1650 : }
1651 :
1652 0 : item->setText(text);
1653 0 : text->setTrack(item->track());
1654 0 : DO_ASSERT(item->hand() != TappingHand::INVALID);
1655 0 : text->setXmlText(item->hand() == TappingHand::LEFT ? "l.h. tap" : "r.h. tap");
1656 0 : text->setAlign(Align(AlignH::HCENTER, AlignV::BASELINE));
1657 :
1658 0 : layoutTextBase(text, ctx, text->mutldata());
1659 :
1660 0 : item->setbbox(text->ldata()->bbox());
1661 0 : }
1662 :
1663 0 : void SingleLayout::layout(SoundFlag* item, const Context& ctx)
1664 : {
1665 : UNUSED(item);
1666 : UNUSED(ctx);
1667 0 : }
1668 :
1669 0 : void SingleLayout::layout(Stem* item, const Context& ctx)
1670 : {
1671 0 : LayoutContext tctx(ctx.dontUseScore());
1672 0 : TLayout::layoutStem(item, item->mutldata(), tctx.conf());
1673 0 : }
1674 :
1675 0 : void SingleLayout::layout(Sticking* item, const Context& ctx)
1676 : {
1677 0 : layoutTextBase(toTextBase(item), ctx, item->mutldata());
1678 0 : }
1679 :
1680 0 : void SingleLayout::layout(TempoText* item, const Context& ctx)
1681 : {
1682 0 : layoutTextBase(item, ctx, item->mutldata());
1683 0 : }
1684 :
1685 0 : void SingleLayout::layout(TextLine* item, const Context& ctx)
1686 : {
1687 0 : layoutLine(item, ctx);
1688 0 : }
1689 :
1690 0 : void SingleLayout::layout(TextLineSegment* item, const Context& ctx)
1691 : {
1692 0 : layoutTextLineBaseSegment(item, ctx);
1693 0 : item->setOffset(PointF());
1694 0 : }
1695 :
1696 0 : void SingleLayout::layout(TimeSig* item, const Context& ctx)
1697 : {
1698 0 : TimeSig::LayoutData* ldata = item->mutldata();
1699 :
1700 0 : double spatium = item->spatium();
1701 :
1702 0 : ldata->setPos(0.0, 0.0);
1703 0 : ldata->setBbox(RectF()); // prepare for an empty time signature
1704 :
1705 0 : constexpr double lineDist = 1.0;
1706 0 : constexpr int numOfLines = 5;
1707 0 : TimeSigType sigType = item->timeSigType();
1708 :
1709 : // if some symbol
1710 : // compute vert. displacement to center in the staff height
1711 : // determine middle staff position:
1712 :
1713 0 : double yoff = spatium * (numOfLines - 1) * 0.5 * lineDist;
1714 :
1715 : // C and Ccut are placed at the middle of the staff: use yoff directly
1716 0 : IEngravingFontPtr font = ctx.engravingFont();
1717 0 : SizeF mag(item->magS() * item->scale());
1718 :
1719 0 : if (sigType == TimeSigType::FOUR_FOUR) {
1720 0 : ldata->pz = PointF(0.0, yoff);
1721 0 : RectF bbox = font->bbox(SymId::timeSigCommon, mag);
1722 0 : ldata->setBbox(bbox.translated(ldata->pz));
1723 0 : ldata->ns.clear();
1724 0 : ldata->ns.push_back(SymId::timeSigCommon);
1725 0 : ldata->ds.clear();
1726 0 : } else if (sigType == TimeSigType::ALLA_BREVE) {
1727 0 : ldata->pz = PointF(0.0, yoff);
1728 0 : RectF bbox = font->bbox(SymId::timeSigCutCommon, mag);
1729 0 : ldata->setBbox(bbox.translated(ldata->pz));
1730 0 : ldata->ns.clear();
1731 0 : ldata->ns.push_back(SymId::timeSigCutCommon);
1732 0 : ldata->ds.clear();
1733 0 : } else if (sigType == TimeSigType::CUT_BACH) {
1734 0 : ldata->pz = PointF(0.0, yoff);
1735 0 : RectF bbox = font->bbox(SymId::timeSigCut2, mag);
1736 0 : ldata->setBbox(bbox.translated(ldata->pz));
1737 0 : ldata->ns.clear();
1738 0 : ldata->ns.push_back(SymId::timeSigCut2);
1739 0 : ldata->ds.clear();
1740 0 : } else if (sigType == TimeSigType::CUT_TRIPLE) {
1741 0 : ldata->pz = PointF(0.0, yoff);
1742 0 : RectF bbox = font->bbox(SymId::timeSigCut3, mag);
1743 0 : ldata->setBbox(bbox.translated(ldata->pz));
1744 0 : ldata->ns.clear();
1745 0 : ldata->ns.push_back(SymId::timeSigCut3);
1746 0 : ldata->ds.clear();
1747 : } else {
1748 0 : if (item->numeratorString().isEmpty()) {
1749 0 : ldata->ns = timeSigSymIdsFromString(item->numeratorString().isEmpty()
1750 0 : ? String::number(item->sig().numerator())
1751 0 : : item->numeratorString());
1752 :
1753 0 : ldata->ds = timeSigSymIdsFromString(item->denominatorString().isEmpty()
1754 0 : ? String::number(item->sig().denominator())
1755 0 : : item->denominatorString());
1756 : } else {
1757 0 : ldata->ns = timeSigSymIdsFromString(item->numeratorString());
1758 0 : ldata->ds = timeSigSymIdsFromString(item->denominatorString());
1759 : }
1760 :
1761 0 : RectF numRect = font->bbox(ldata->ns, mag);
1762 0 : RectF denRect = font->bbox(ldata->ds, mag);
1763 :
1764 : // position numerator and denominator; vertical displacement:
1765 : // number of lines is odd: 0.0 (strings are directly above and below the middle line)
1766 : // number of lines even: 0.05 (strings are moved up/down to leave 1/10sp between them)
1767 :
1768 0 : double displ = (numOfLines & 1) ? 0.0 : (0.05 * spatium);
1769 :
1770 : //align on the wider
1771 0 : double pzY = yoff - (denRect.width() < 0.01 ? 0.0 : (displ + numRect.height() * 0.5));
1772 0 : double pnY = yoff + displ + denRect.height() * 0.5;
1773 :
1774 0 : if (numRect.width() >= denRect.width()) {
1775 : // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
1776 0 : ldata->pz = PointF(0.0, pzY);
1777 : // denominator: horiz: centred around centre of numerator | vert: one space below centre line
1778 0 : ldata->pn = PointF((numRect.width() - denRect.width()) * 0.5, pnY);
1779 : } else {
1780 : // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
1781 0 : ldata->pz = PointF((denRect.width() - numRect.width()) * 0.5, pzY);
1782 : // denominator: horiz: centred around centre of numerator | vert: one space below centre line
1783 0 : ldata->pn = PointF(0.0, pnY);
1784 : }
1785 :
1786 : // centering of parenthesis so the middle of the parenthesis is at the divisor marking level
1787 0 : int centerY = yoff / 2 + spatium;
1788 0 : int widestPortion = numRect.width() > denRect.width() ? numRect.width() : denRect.width();
1789 0 : ldata->pointLargeLeftParen = PointF(-spatium, centerY);
1790 0 : ldata->pointLargeRightParen = PointF(widestPortion + spatium, centerY);
1791 :
1792 0 : ldata->setBbox(numRect.translated(ldata->pz)); // translate bounding boxes to actual string positions
1793 0 : ldata->addBbox(denRect.translated(ldata->pn));
1794 0 : if (item->largeParentheses()) {
1795 0 : ldata->addBbox(RectF(ldata->pointLargeLeftParen.x(), ldata->pointLargeLeftParen.y() - denRect.height(), spatium / 2,
1796 0 : numRect.height() + denRect.height()));
1797 0 : ldata->addBbox(RectF(ldata->pointLargeRightParen.x(), ldata->pointLargeRightParen.y() - denRect.height(), spatium / 2,
1798 0 : numRect.height() + denRect.height()));
1799 : }
1800 : }
1801 0 : }
1802 :
1803 0 : void SingleLayout::layout(TremoloSingleChord* item, const Context&)
1804 : {
1805 0 : item->computeShape(); // set bbox
1806 0 : item->setPath(item->basePath());
1807 0 : }
1808 :
1809 0 : void SingleLayout::layout(TremoloTwoChord* item, const Context&)
1810 : {
1811 0 : item->computeShape(); // set bbox
1812 0 : item->setPath(item->basePath());
1813 0 : }
1814 :
1815 0 : void SingleLayout::layout(TremoloBar* item, const Context&)
1816 : {
1817 0 : TremoloBar::LayoutData* ldata = item->mutldata();
1818 0 : double spatium = item->spatium();
1819 :
1820 0 : ldata->setPos(PointF());
1821 :
1822 0 : double timeFactor = item->userMag() / 1.0;
1823 0 : double pitchFactor = -spatium * 0.02;
1824 :
1825 0 : PolygonF polygon;
1826 0 : for (const PitchValue& v : item->points()) {
1827 0 : polygon << PointF(v.time * timeFactor, v.pitch * pitchFactor);
1828 : }
1829 0 : ldata->polygon = polygon;
1830 :
1831 0 : const double w = item->absoluteFromSpatium(item->lineWidth());
1832 0 : ldata->setBbox(ldata->polygon.boundingRect().adjusted(-w, -w, w, w));
1833 0 : }
1834 :
1835 0 : void SingleLayout::layout(Trill* item, const Context& ctx)
1836 : {
1837 0 : layoutLine(static_cast<SLine*>(item), ctx);
1838 0 : }
1839 :
1840 0 : void SingleLayout::layout(TrillSegment* item, const Context& ctx)
1841 : {
1842 0 : TrillSegment::LayoutData* ldata = item->mutldata();
1843 0 : if (item->spanner()->placeBelow()) {
1844 0 : ldata->setPosY(0.0);
1845 : }
1846 :
1847 0 : bool accidentalGoesBelow = item->trill()->trillType() == TrillType::DOWNPRALL_LINE;
1848 0 : Trill* trill = item->trill();
1849 0 : Ornament* ornament = trill->ornament();
1850 0 : if (ornament) {
1851 0 : if (item->isSingleBeginType()) {
1852 0 : layout(ornament, ctx);
1853 : }
1854 0 : trill->setAccidental(accidentalGoesBelow ? ornament->accidentalBelow() : ornament->accidentalAbove());
1855 0 : trill->setCueNoteChord(ornament->cueNoteChord());
1856 0 : ArticulationAnchor anchor = ornament->anchor();
1857 0 : if (anchor == ArticulationAnchor::AUTO) {
1858 0 : trill->setPlacement(trill->track() % 2 ? PlacementV::BELOW : PlacementV::ABOVE);
1859 : } else {
1860 0 : trill->setPlacement(anchor == ArticulationAnchor::TOP ? PlacementV::ABOVE : PlacementV::BELOW);
1861 : }
1862 0 : trill->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::STYLED); // Ensures that the property isn't written (it is written by the ornamnent)
1863 : }
1864 :
1865 0 : if (item->isSingleType() || item->isBeginType()) {
1866 0 : switch (item->trill()->trillType()) {
1867 0 : case TrillType::TRILL_LINE:
1868 0 : item->symbolLine(SymId::ornamentTrill, SymId::wiggleTrill);
1869 0 : break;
1870 0 : case TrillType::PRALLPRALL_LINE:
1871 0 : item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
1872 0 : break;
1873 0 : case TrillType::UPPRALL_LINE:
1874 0 : item->symbolLine(SymId::ornamentBottomLeftConcaveStroke,
1875 : SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
1876 0 : break;
1877 0 : case TrillType::DOWNPRALL_LINE:
1878 0 : item->symbolLine(SymId::ornamentLeftVerticalStroke,
1879 : SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
1880 0 : break;
1881 : }
1882 0 : Accidental* a = item->trill()->accidental();
1883 0 : if (a) {
1884 0 : double vertMargin = 0.35 * item->spatium();
1885 0 : RectF box = item->symBbox(item->symbols().front());
1886 0 : double x = 0;
1887 0 : double y = 0;
1888 0 : x = 0.5 * (box.width() - a->width());
1889 : double minVertDist = accidentalGoesBelow
1890 0 : ? Shape(box).minVerticalDistance(a->shape())
1891 0 : : a->shape().minVerticalDistance(Shape(box));
1892 0 : y = accidentalGoesBelow ? minVertDist + vertMargin : -minVertDist - vertMargin;
1893 0 : a->setPos(x, y);
1894 0 : a->setOwnershipParent(item);
1895 : }
1896 : } else {
1897 0 : item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
1898 : }
1899 :
1900 0 : item->setOffset(PointF());
1901 0 : }
1902 :
1903 0 : void SingleLayout::layout(Vibrato* item, const Context& ctx)
1904 : {
1905 0 : layoutLine(static_cast<SLine*>(item), ctx);
1906 0 : }
1907 :
1908 0 : void SingleLayout::layout(VibratoSegment* item, const Context&)
1909 : {
1910 0 : VibratoSegment::LayoutData* ldata = item->mutldata();
1911 0 : if (item->spanner()->placeBelow()) {
1912 0 : ldata->setPosY(0.0);
1913 : }
1914 :
1915 0 : switch (item->vibrato()->vibratoType()) {
1916 0 : case VibratoType::GUITAR_VIBRATO:
1917 0 : item->symbolLine(SymId::guitarVibratoStroke, SymId::guitarVibratoStroke);
1918 0 : break;
1919 0 : case VibratoType::GUITAR_VIBRATO_WIDE:
1920 0 : item->symbolLine(SymId::guitarWideVibratoStroke, SymId::guitarWideVibratoStroke);
1921 0 : break;
1922 0 : case VibratoType::VIBRATO_SAWTOOTH:
1923 0 : item->symbolLine(SymId::wiggleSawtooth, SymId::wiggleSawtooth);
1924 0 : break;
1925 0 : case VibratoType::VIBRATO_SAWTOOTH_WIDE:
1926 0 : item->symbolLine(SymId::wiggleSawtoothWide, SymId::wiggleSawtoothWide);
1927 0 : break;
1928 0 : default:
1929 0 : break;
1930 : }
1931 :
1932 0 : item->setOffset(PointF());
1933 0 : }
1934 :
1935 0 : void SingleLayout::layout(Volta* item, const Context& ctx)
1936 : {
1937 0 : layoutLine(item, ctx);
1938 0 : }
1939 :
1940 0 : void SingleLayout::layout(WhammyBar* item, const Context& ctx)
1941 : {
1942 0 : layoutLine(item, ctx);
1943 0 : }
1944 :
1945 0 : void SingleLayout::layout(VoltaSegment* item, const Context& ctx)
1946 : {
1947 0 : layoutTextLineBaseSegment(item, ctx);
1948 0 : item->setOffset(PointF());
1949 :
1950 0 : double spatium = ctx.style().spatium();
1951 0 : double hookHeight = item->absoluteFromSpatium(item->volta()->beginHookHeight());
1952 0 : if (item->text()) {
1953 0 : Text* text = item->text();
1954 0 : RectF textBBox = text->ldata()->bbox().translated(text->pos());
1955 0 : text->mutldata()->moveY(hookHeight - textBBox.bottom());
1956 0 : text->mutldata()->moveX(0.5 * spatium);
1957 : }
1958 0 : }
1959 :
1960 0 : void SingleLayout::layout(WhammyBarSegment* item, const Context& ctx)
1961 : {
1962 0 : layoutTextLineBaseSegment(item, ctx);
1963 0 : item->setOffset(PointF());
1964 0 : }
1965 :
1966 0 : void SingleLayout::layout(Text* item, const Context& ctx)
1967 : {
1968 0 : layoutTextBase(toTextBase(item), ctx, item->mutldata());
1969 0 : }
1970 :
1971 0 : void SingleLayout::layoutTextBase(const TextBase* item, const Context& ctx, TextBase::LayoutData* ldata)
1972 : {
1973 0 : ldata->setPos(PointF());
1974 0 : const_cast<TextBase*>(item)->setOffset(PointF());
1975 :
1976 0 : if (item->placeBelow()) {
1977 0 : ldata->setPosY(0.0);
1978 : }
1979 :
1980 0 : layout1TextBase(item, ctx, ldata);
1981 0 : }
1982 :
1983 0 : static void textHorizontalLayout(const TextBase* item, Shape& shape, double maxBlockWidth, TextBase::LayoutData* ldata)
1984 : {
1985 : // Position and alignment
1986 0 : for (size_t i = 0; i < ldata->blocks.size(); ++i) {
1987 0 : TextBlock& textBlock = ldata->blocks[i];
1988 0 : double xAdj = -textBlock.boundingRect().left();
1989 :
1990 : // Set position relative to reference point
1991 0 : AlignH position = item->position();
1992 0 : if (position == AlignH::HCENTER) {
1993 0 : xAdj -= maxBlockWidth * .5;
1994 0 : } else if (position == AlignH::RIGHT) {
1995 0 : xAdj -= maxBlockWidth;
1996 : }
1997 :
1998 0 : double diff = maxBlockWidth - textBlock.boundingRect().width();
1999 0 : if (muse::RealIsNull(diff)) {
2000 : // This is the longest line, don't align
2001 0 : for (TextFragment& f : textBlock.fragments()) {
2002 0 : f.pos.rx() += xAdj;
2003 : }
2004 0 : textBlock.shape().translate(PointF(xAdj, 0.0));
2005 0 : shape.add(textBlock.shape().translated(PointF(0.0, textBlock.y())));
2006 0 : continue;
2007 0 : }
2008 : // Align relative to the longest line
2009 0 : AlignH alignH = item->align().horizontal;
2010 0 : if (alignH == AlignH::HCENTER) {
2011 0 : xAdj += diff * 0.5;
2012 0 : } else if (alignH == AlignH::RIGHT) {
2013 0 : xAdj += diff;
2014 : }
2015 :
2016 0 : for (TextFragment& fragment : textBlock.fragments()) {
2017 0 : fragment.pos.rx() += xAdj;
2018 : }
2019 0 : textBlock.shape().translate(PointF(xAdj, 0.0));
2020 0 : shape.add(textBlock.shape().translated(PointF(0.0, textBlock.y())));
2021 : }
2022 0 : }
2023 :
2024 0 : void SingleLayout::layout1TextBase(const TextBase* item, const Context&, TextBase::LayoutData* ldata)
2025 : {
2026 0 : if (ldata->layoutInvalid) {
2027 0 : item->createBlocks(ldata);
2028 : }
2029 :
2030 0 : double y = 0;
2031 :
2032 0 : double maxBlockWidth = -DBL_MAX;
2033 : // adjust the bounding box for the text item
2034 0 : for (size_t i = 0; i < ldata->rows(); ++i) {
2035 0 : TextBlock& t = ldata->blocks[i];
2036 0 : TextLayout::layoutTextBlock(&t, item);
2037 0 : const RectF* r = &t.boundingRect();
2038 :
2039 0 : if (r->height() == 0) {
2040 0 : r = &ldata->blocks.at(0).boundingRect();
2041 : }
2042 0 : y += t.lineSpacing();
2043 0 : t.setY(y);
2044 0 : maxBlockWidth = std::max(maxBlockWidth, t.boundingRect().width());
2045 : }
2046 :
2047 0 : Shape shape;
2048 0 : textHorizontalLayout(item, shape, maxBlockWidth, ldata);
2049 :
2050 0 : RectF bb = shape.bbox();
2051 :
2052 0 : double yoff = 0;
2053 0 : double h = 0;
2054 :
2055 0 : ldata->setPos(PointF());
2056 :
2057 0 : if (item->align() == AlignV::BOTTOM) {
2058 0 : yoff += h - bb.bottom();
2059 0 : } else if (item->align() == AlignV::VCENTER) {
2060 0 : yoff += (h - (bb.top() + bb.bottom())) * .5;
2061 0 : } else if (item->align() == AlignV::BASELINE) {
2062 0 : yoff += h * .5 - ldata->blocks.front().lineSpacing();
2063 : } else {
2064 0 : yoff += -bb.top();
2065 : }
2066 :
2067 0 : for (TextBlock& t : ldata->blocks) {
2068 0 : t.setY(t.y() + yoff);
2069 : }
2070 :
2071 0 : shape.translateY(yoff);
2072 0 : ldata->setShape(shape);
2073 :
2074 0 : if (item->hasFrame()) {
2075 0 : item->layoutFrame(ldata);
2076 : }
2077 :
2078 0 : if (!muse::RealIsNull(item->textAngle())) {
2079 0 : Transform t;
2080 0 : t.rotate(item->textAngle());
2081 0 : ldata->setShape(shape.transform(t));
2082 0 : ldata->highResShape.mut_value().transform(t);
2083 : }
2084 0 : }
2085 :
2086 0 : void SingleLayout::layoutLine(SLine* item, const Context& ctx)
2087 : {
2088 0 : if (item->spannerSegments().empty()) {
2089 0 : item->setLen(ctx.style().spatium() * 8);
2090 : }
2091 :
2092 0 : LineSegment* lineSegm = item->frontSegment();
2093 0 : layoutLineSegment(lineSegm, ctx);
2094 :
2095 0 : item->setbbox(lineSegm->ldata()->bbox(LD_ACCESS::BAD));
2096 0 : }
2097 :
2098 : // Extends lines to fill the corner between them.
2099 : // Assumes that l1p2 == l2p1 is the intersection between the lines.
2100 : // If checkAngle is false, assumes that the lines are perpendicular,
2101 : // and some calculations are saved.
2102 0 : static inline void extendLines(const PointF& l1p1, PointF& l1p2, PointF& l2p1, const PointF& l2p2, double lineWidth, bool checkAngle)
2103 : {
2104 0 : PointF l1UnitVector = (l1p2 - l1p1).normalized();
2105 0 : PointF l2UnitVector = (l2p1 - l2p2).normalized();
2106 :
2107 0 : double addedLength = lineWidth * 0.5;
2108 :
2109 0 : if (checkAngle) {
2110 0 : double angle = M_PI - acos(PointF::dotProduct(l1UnitVector, l2UnitVector));
2111 :
2112 0 : if (angle <= M_PI_2) {
2113 0 : addedLength *= tan(0.5 * angle);
2114 : }
2115 : }
2116 :
2117 0 : l1p2 += l1UnitVector * addedLength;
2118 0 : l2p1 += l2UnitVector * addedLength;
2119 0 : }
2120 :
2121 0 : static PolygonF createArrow(bool start, bool filled, PointF& startPoint, PointF& endPoint, const TextLineBase* tl)
2122 : {
2123 0 : double arrowWidth = 0.0;
2124 0 : double arrowHeight = 0.0;
2125 0 : if (filled) {
2126 0 : arrowWidth = tl->absoluteFromSpatium(start ? tl->beginFilledArrowWidth() : tl->endFilledArrowWidth());
2127 0 : arrowHeight = tl->absoluteFromSpatium(start ? tl->beginFilledArrowHeight() : tl->endFilledArrowHeight());
2128 : } else {
2129 0 : arrowWidth = tl->absoluteFromSpatium(start ? tl->beginLineArrowWidth() : tl->endLineArrowWidth());
2130 0 : arrowHeight = tl->absoluteFromSpatium(start ? tl->beginLineArrowHeight() : tl->endLineArrowHeight());
2131 : }
2132 :
2133 0 : PolygonF arrow;
2134 0 : if (start) {
2135 0 : arrow << PointF(0.0, -arrowHeight / 2) << PointF(-arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2); // left
2136 : } else {
2137 0 : arrow << PointF(0.0, -arrowHeight / 2) << PointF(arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2); // right
2138 : }
2139 :
2140 0 : PointF arrowAdjust = PointF(arrowWidth, 0.0);
2141 0 : arrowAdjust = (start ? 1.0 : -1.0) * arrowAdjust;
2142 0 : arrow.translate(arrowAdjust);
2143 :
2144 0 : const double yDiff = endPoint.y() - startPoint.y();
2145 0 : const double xDiff = endPoint.x() - startPoint.x();
2146 0 : const double rotate = atan(yDiff / xDiff) + (endPoint.x() < startPoint.x() ? M_PI : 0.0);
2147 :
2148 0 : Transform t;
2149 0 : t.rotateRadians(rotate);
2150 :
2151 0 : for (PointF& p : arrow) {
2152 0 : p = t.map(p);
2153 : }
2154 :
2155 0 : if (start) {
2156 0 : arrow.translate(startPoint);
2157 : } else {
2158 0 : arrow.translate(endPoint);
2159 : }
2160 :
2161 0 : const PointF lineVector = endPoint - startPoint;
2162 0 : const PointF unitVector = lineVector.normalized();
2163 0 : const double reduction = filled ? arrowWidth : tl->absoluteFromSpatium(tl->lineWidth()) / 2;
2164 0 : if (start) {
2165 0 : startPoint += reduction * unitVector;
2166 : } else {
2167 0 : endPoint -= reduction * unitVector;
2168 : }
2169 :
2170 0 : return arrow;
2171 0 : }
2172 :
2173 0 : void SingleLayout::layoutTextLineBaseSegment(TextLineBaseSegment* item, const Context& ctx)
2174 : {
2175 0 : TextLineBaseSegment::LayoutData* ldata = item->mutldata();
2176 0 : ldata->npoints = 0;
2177 0 : const TextLineBase* tl = item->textLineBase();
2178 0 : const double spatium = tl->spatium();
2179 0 : const bool isSingleOrBegin = item->isSingleBeginType();
2180 0 : const bool isSingleOrEnd = item->isSingleEndType();
2181 0 : const bool isDottedLine = tl->lineStyle() == LineType::DOTTED;
2182 0 : const double lineWidth = tl->absoluteFromSpatium(tl->lineWidth());
2183 :
2184 0 : if (!tl->diagonal()) {
2185 0 : item->setUserYoffset2(0);
2186 : }
2187 :
2188 0 : auto alignBaseLine = [lineWidth](Text* text, PointF& pp1, PointF& pp2) {
2189 0 : PointF widthCorrection(0.0, lineWidth / 2);
2190 0 : switch (text->align().vertical) {
2191 0 : case AlignV::TOP:
2192 0 : pp1 += widthCorrection;
2193 0 : pp2 += widthCorrection;
2194 0 : break;
2195 0 : case AlignV::VCENTER:
2196 0 : break;
2197 0 : case AlignV::BOTTOM:
2198 0 : pp1 -= widthCorrection;
2199 0 : pp2 -= widthCorrection;
2200 0 : break;
2201 0 : case AlignV::BASELINE:
2202 0 : pp1 -= widthCorrection;
2203 0 : pp2 -= widthCorrection;
2204 0 : break;
2205 : }
2206 0 : };
2207 :
2208 0 : if (isSingleOrBegin) {
2209 0 : item->text()->setXmlText(tl->beginText());
2210 0 : item->text()->setFamily(tl->beginFontFamily());
2211 0 : item->text()->setSize(tl->beginFontSize());
2212 0 : item->text()->setSymbolScale(tl->beginTextMusicalSymbolsScale());
2213 0 : item->text()->setSymbolSize(tl->beginTextMusicSymbolsSize());
2214 0 : item->text()->setOffset(tl->beginTextOffset() * item->mag());
2215 0 : item->text()->setAlign(tl->beginTextAlign());
2216 0 : item->text()->setPosition(tl->beginTextPosition());
2217 0 : item->text()->setFontStyle(tl->beginFontStyle());
2218 : } else {
2219 0 : item->text()->setXmlText(tl->continueText());
2220 0 : item->text()->setFamily(tl->continueFontFamily());
2221 0 : item->text()->setSize(tl->continueFontSize());
2222 0 : item->text()->setSymbolScale(tl->continueTextMusicalSymbolsScale());
2223 0 : item->text()->setSymbolSize(tl->continueTextMusicSymbolsSize());
2224 0 : item->text()->setOffset(tl->continueTextOffset() * item->mag());
2225 0 : item->text()->setAlign(tl->continueTextAlign());
2226 0 : item->text()->setPosition(tl->continueTextPosition());
2227 0 : item->text()->setFontStyle(tl->continueFontStyle());
2228 : }
2229 0 : item->text()->setPlacement(PlacementV::ABOVE);
2230 0 : item->text()->setTrack(item->track());
2231 0 : item->text()->setColor(tl->lineColor());
2232 0 : layout(item->text(), ctx);
2233 :
2234 0 : if (isSingleOrEnd) {
2235 0 : item->endText()->setXmlText(tl->endText());
2236 0 : item->endText()->setFamily(tl->endFontFamily());
2237 0 : item->endText()->setSize(tl->endFontSize());
2238 0 : item->endText()->setSymbolScale(tl->endTextMusicalSymbolsScale());
2239 0 : item->endText()->setSymbolSize(tl->endTextMusicSymbolsSize());
2240 0 : item->endText()->setOffset(tl->endTextOffset() * item->mag());
2241 0 : item->endText()->setAlign(tl->endTextAlign());
2242 0 : item->endText()->setPosition(tl->endTextPosition());
2243 0 : item->endText()->setFontStyle(tl->endFontStyle());
2244 0 : item->endText()->setPlacement(PlacementV::ABOVE);
2245 0 : item->endText()->setTrack(item->track());
2246 0 : item->endText()->setColor(tl->lineColor());
2247 0 : layout(item->endText(), ctx);
2248 : } else {
2249 0 : item->endText()->setXmlText(u"");
2250 : }
2251 :
2252 0 : if (!item->textLineBase()->textSizeSpatiumDependent()) {
2253 0 : item->text()->setSize(item->text()->size() * item->defaultSpatium() / item->spatium());
2254 0 : item->endText()->setSize(item->endText()->size() * item->defaultSpatium() / item->spatium());
2255 : }
2256 :
2257 0 : PointF pp1;
2258 0 : PointF pp2(item->pos2());
2259 :
2260 : // line with no text or hooks - just use the basic rectangle for line
2261 0 : if (item->text()->empty() && item->endText()->empty()
2262 0 : && (!isSingleOrBegin || tl->beginHookType() == HookType::NONE)
2263 0 : && (!isSingleOrEnd || tl->endHookType() == HookType::NONE)) {
2264 0 : ldata->npoints = 2;
2265 0 : ldata->points[0] = pp1;
2266 0 : ldata->points[1] = pp2;
2267 0 : ldata->lineLength = sqrt(PointF::dotProduct(pp2 - pp1, pp2 - pp1));
2268 :
2269 0 : item->setbbox(TextLineBaseSegment::boundingBoxOfLine(pp1, pp2, lineWidth / 2, isDottedLine));
2270 0 : return;
2271 : }
2272 :
2273 0 : double l1 = 0.0;
2274 0 : double l2 = 0.0;
2275 0 : const double gapBetweenTextAndLine = spatium * tl->gapBetweenTextAndLine().val();
2276 :
2277 0 : const bool alignBeginText = tl->beginTextPlace() == TextPlace::LEFT || tl->beginTextPlace() == TextPlace::AUTO;
2278 0 : const bool alignContinueText = tl->continueTextPlace() == TextPlace::LEFT || tl->continueTextPlace() == TextPlace::AUTO;
2279 0 : const bool alignEndText = tl->endTextPlace() == TextPlace::LEFT || tl->endTextPlace() == TextPlace::AUTO;
2280 0 : const bool hasBeginText = !item->text()->empty() && isSingleOrBegin;
2281 0 : const bool hasContinueText = !item->text()->empty() && !isSingleOrBegin;
2282 0 : const bool hasEndText = !item->endText()->empty() && isSingleOrEnd;
2283 :
2284 0 : Shape shape;
2285 :
2286 0 : if (!item->text()->empty()) {
2287 0 : if ((isSingleOrBegin && alignBeginText) || (!isSingleOrBegin && alignContinueText)) {
2288 0 : l1 = gapBetweenTextAndLine;
2289 0 : switch (item->text()->position()) {
2290 0 : case AlignH::LEFT:
2291 0 : l1 += item->text()->ldata()->bbox().width();
2292 0 : break;
2293 0 : case AlignH::HCENTER:
2294 0 : l1 += item->text()->ldata()->bbox().width() / 2;
2295 0 : break;
2296 0 : default:
2297 0 : break;
2298 : }
2299 : }
2300 0 : shape.add(item->text()->ldata()->bbox().translated(item->text()->pos()), item);
2301 : }
2302 : // set end text position and extend bbox
2303 0 : if (!item->endText()->empty()) {
2304 0 : if (alignEndText) {
2305 0 : l2 = gapBetweenTextAndLine;
2306 0 : switch (item->endText()->position()) {
2307 0 : case AlignH::RIGHT:
2308 0 : l2 += item->endText()->ldata()->bbox().width();
2309 0 : break;
2310 0 : case AlignH::HCENTER:
2311 0 : l2 += item->endText()->ldata()->bbox().width() / 2;
2312 0 : break;
2313 0 : default:
2314 0 : break;
2315 : }
2316 : }
2317 0 : const double endTextX = std::max(pp2.x(), shape.bbox().right() + l2); // prevent end text from overlapping begin text
2318 0 : item->endText()->mutldata()->setPosX(endTextX);
2319 0 : shape.add(item->endText()->ldata()->bbox().translated(item->endText()->pos()), item);
2320 : }
2321 :
2322 0 : bool beginHookDrawnSeparately = false;
2323 0 : bool endHookDrawnSeparately = false;
2324 :
2325 0 : if (tl->lineVisible()) {
2326 0 : pp1.rx() += l1;
2327 0 : pp2.rx() -= l2;
2328 :
2329 : // Make sure baseline of text and line are properly aligned (accounting for line thickness)
2330 0 : if ((hasBeginText && alignBeginText) || (hasContinueText && alignContinueText)) {
2331 0 : alignBaseLine(item->text(), pp1, pp2);
2332 0 : } else if (hasEndText && alignEndText) {
2333 0 : alignBaseLine(item->endText(), pp1, pp2);
2334 : }
2335 :
2336 0 : const double beginHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->beginHookHeight().val() * spatium;
2337 0 : const double endHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->endHookHeight().val() * spatium;
2338 :
2339 0 : double beginHookWidth = 0.0;
2340 0 : double endHookWidth = 0.0;
2341 :
2342 0 : if (tl->beginHookType() == HookType::HOOK_45) {
2343 0 : beginHookWidth = std::fabs(beginHookHeight * .4);
2344 0 : pp1.rx() += beginHookWidth;
2345 : }
2346 :
2347 0 : if (tl->endHookType() == HookType::HOOK_45) {
2348 0 : endHookWidth = std::fabs(endHookHeight * .4);
2349 0 : pp2.rx() -= endHookWidth;
2350 : }
2351 :
2352 : // don't draw backwards lines (or hooks) if text is longer than nominal line length
2353 0 : if (!item->text()->empty() && pp1.x() > pp2.x() && !tl->diagonal()) {
2354 0 : ldata->setShape(shape);
2355 0 : return;
2356 : }
2357 :
2358 : const bool beginArrow = isSingleOrBegin
2359 0 : && (tl->beginHookType() == HookType::ARROW_FILLED || tl->beginHookType() == HookType::ARROW);
2360 0 : const bool endArrow = isSingleOrEnd && (tl->endHookType() == HookType::ARROW_FILLED || tl->endHookType() == HookType::ARROW);
2361 :
2362 0 : if (beginArrow) {
2363 0 : const bool filled = tl->beginHookType() == HookType::ARROW_FILLED;
2364 0 : ldata->beginArrow = createArrow(true, filled, pp1, pp2, tl);
2365 0 : shape.add(ldata->beginArrow.boundingRect(), item);
2366 : }
2367 0 : if (endArrow) {
2368 0 : const bool filled = tl->endHookType() == HookType::ARROW_FILLED;
2369 0 : ldata->endArrow = createArrow(false, filled, pp1, pp2, tl);
2370 0 : shape.add(ldata->endArrow.boundingRect(), item);
2371 : }
2372 :
2373 0 : auto hasHook = [](HookType type) -> bool {
2374 0 : switch (type) {
2375 0 : case HookType::HOOK_45:
2376 : case HookType::HOOK_90:
2377 : case HookType::HOOK_90T:
2378 0 : return true;
2379 0 : case HookType::NONE:
2380 : case HookType::ARROW:
2381 : case HookType::ARROW_FILLED:
2382 : case HookType::ROSETTE:
2383 0 : return false;
2384 0 : default:
2385 0 : break;
2386 : }
2387 0 : return false;
2388 : };
2389 :
2390 0 : const bool beginHook = isSingleOrBegin && hasHook(tl->beginHookType());
2391 0 : const bool endHook = isSingleOrEnd && hasHook(tl->endHookType());
2392 :
2393 0 : if (beginHook) {
2394 : // We use the term "endpoint" for the point that does not touch the main line.
2395 0 : const PointF& beginHookEndpoint = ldata->points[ldata->npoints++]
2396 0 : = PointF(pp1.x() - beginHookWidth, pp1.y() + beginHookHeight);
2397 0 : if (tl->beginHookType() == HookType::HOOK_90T) {
2398 : // A T-hook needs to be drawn separately, so we add an extra point
2399 0 : beginHookDrawnSeparately = true;
2400 0 : ldata->points[ldata->npoints++] = PointF(pp1.x() - beginHookWidth, pp1.y() - beginHookHeight);
2401 0 : } else if (tl->lineStyle() != LineType::SOLID) {
2402 : // For non-solid lines, we also draw the hook separately,
2403 : // so that we can distribute the dashes/dots for each linepiece individually
2404 0 : beginHookDrawnSeparately = true;
2405 0 : PointF& beginHookStartpoint = ldata->points[ldata->npoints++] = pp1;
2406 :
2407 0 : if (tl->lineStyle() == LineType::DASHED) {
2408 : // For dashes lines, we extend the lines somewhat,
2409 : // so that the corner between them gets filled
2410 0 : bool checkAngle = tl->beginHookType() == HookType::HOOK_45 || tl->diagonal();
2411 0 : extendLines(beginHookEndpoint, beginHookStartpoint, pp1, pp2, lineWidth, checkAngle);
2412 : }
2413 : }
2414 : }
2415 :
2416 0 : ldata->points[ldata->npoints++] = pp1;
2417 0 : PointF& pp22 = ldata->points[ldata->npoints++] = pp2; // Keep a reference so that we can modify later
2418 :
2419 0 : if (endHook) {
2420 0 : const PointF endHookEndpoint = PointF(pp2.x() + endHookWidth, pp2.y() + endHookHeight);
2421 0 : if (tl->endHookType() == HookType::HOOK_90T) {
2422 : // A T-hook needs to be drawn separately, so we add an extra point
2423 0 : endHookDrawnSeparately = true;
2424 0 : ldata->points[ldata->npoints++] = PointF(pp2.x() + endHookWidth, pp2.y() - endHookHeight);
2425 0 : } else if (tl->lineStyle() != LineType::SOLID) {
2426 : // For non-solid lines, we also draw the hook separately,
2427 : // so that we can distribute the dashes/dots for each linepiece individually
2428 0 : endHookDrawnSeparately = true;
2429 0 : PointF& endHookStartpoint = ldata->points[ldata->npoints++] = pp2;
2430 :
2431 0 : if (tl->lineStyle() == LineType::DASHED) {
2432 : // For dashes lines, we extend the lines somewhat,
2433 : // so that the corner between them gets filled
2434 0 : bool checkAngle = tl->endHookType() == HookType::HOOK_45 || tl->diagonal();
2435 0 : extendLines(pp1, pp22, endHookStartpoint, endHookEndpoint, lineWidth, checkAngle);
2436 : }
2437 : }
2438 :
2439 0 : ldata->points[ldata->npoints++] = endHookEndpoint;
2440 : }
2441 :
2442 0 : ldata->lineLength = sqrt(PointF::dotProduct(pp22 - pp1, pp22 - pp1));
2443 : }
2444 :
2445 : // Calculate shape of line
2446 : {
2447 0 : int start = 0;
2448 0 : if (beginHookDrawnSeparately) {
2449 0 : PointF& p1 = ldata->points[start++];
2450 0 : PointF& p2 = ldata->points[start++];
2451 0 : shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
2452 : }
2453 :
2454 0 : int end = ldata->npoints;
2455 0 : if (endHookDrawnSeparately) {
2456 0 : PointF& p1 = ldata->points[--end];
2457 0 : PointF& p2 = ldata->points[--end];
2458 0 : shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
2459 : }
2460 :
2461 0 : for (int i = start; i < end - 1; ++i) {
2462 0 : shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[i], ldata->points[i + 1], lineWidth / 2,
2463 0 : isDottedLine), item, !tl->lineVisible());
2464 : }
2465 : }
2466 :
2467 0 : ldata->setShape(shape);
2468 0 : }
|