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) 2025 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 "dom/chord.h"
24 : #include "dom/harmony.h"
25 : #include "dom/ledgerline.h"
26 : #include "dom/measure.h"
27 : #include "dom/note.h"
28 : #include "dom/parenthesis.h"
29 : #include "dom/score.h"
30 : #include "dom/segment.h"
31 : #include "dom/slurtie.h"
32 : #include "dom/staff.h"
33 : #include "dom/timesig.h"
34 : #include "horizontalspacing.h"
35 : #include "parenthesislayout.h"
36 :
37 : using namespace mu::engraving;
38 : using namespace mu::engraving::rendering::score;
39 :
40 : static constexpr Spatium MAX_MID_THICKNESS = 0.2_sp;
41 :
42 3626 : void ParenthesisLayout::layoutParentheses(const EngravingItem* parent, const LayoutContext& ctx)
43 : {
44 : // Layout parentheses surrounding an engraving item. Handle padding and placement
45 3626 : Parenthesis* leftParen = parent->leftParen();
46 3626 : Parenthesis* rightParen = parent->rightParen();
47 :
48 3626 : if (!(leftParen || rightParen)) {
49 3556 : return;
50 : }
51 70 : if (leftParen) {
52 45 : layoutParenthesis(leftParen, leftParen->mutldata(), ctx);
53 : }
54 :
55 70 : if (rightParen) {
56 45 : layoutParenthesis(rightParen, rightParen->mutldata(), ctx);
57 : }
58 :
59 70 : bool itemAddToSkyline = parent->autoplace() && !parent->ldata()->isSkipDraw();
60 70 : Shape dummyItemShape = getParentShape(parent);
61 :
62 70 : layoutParentheses(leftParen, rightParen, dummyItemShape, itemAddToSkyline, ctx);
63 70 : }
64 :
65 50677 : void ParenthesisLayout::layoutChordParentheses(const Chord* chord, const LayoutContext& ctx)
66 : {
67 50896 : for (const NoteParenthesisInfo* parenNotesInfo : chord->noteParentheses()) {
68 219 : Parenthesis* leftParen = parenNotesInfo->leftParen();
69 219 : Parenthesis* rightParen = parenNotesInfo->rightParen();
70 :
71 219 : if (!(leftParen || rightParen)) {
72 0 : return;
73 : }
74 219 : if (leftParen) {
75 219 : layoutParenthesis(leftParen, leftParen->mutldata(), ctx);
76 : }
77 :
78 219 : if (rightParen) {
79 219 : layoutParenthesis(rightParen, rightParen->mutldata(), ctx);
80 : }
81 :
82 219 : bool itemAddToSkyline = chord->autoplace() && !chord->ldata()->isSkipDraw();
83 219 : Shape dummyItemShape = getParentShape(chord);
84 :
85 219 : layoutParentheses(leftParen, rightParen, dummyItemShape, itemAddToSkyline, ctx);
86 219 : }
87 : }
88 :
89 289 : void ParenthesisLayout::layoutParentheses(Parenthesis* leftParen, Parenthesis* rightParen, Shape& dummyItemShape, bool itemAddToSkyline,
90 : const LayoutContext& ctx)
91 : {
92 : UNUSED(ctx);
93 289 : if (!leftParen || !rightParen) {
94 : // 1 parenthesis
95 50 : Parenthesis* paren = leftParen ? leftParen : rightParen;
96 50 : Parenthesis::LayoutData* ldata = paren->mutldata();
97 50 : const bool leftBracket = paren->direction() == DirectionH::LEFT;
98 50 : double minDist = 0.0;
99 50 : if (!leftBracket && itemAddToSkyline) {
100 : // Space against existing item shape
101 25 : minDist = HorizontalSpacing::minHorizontalDistance(dummyItemShape, paren->shape().translated(
102 : ldata->pos()), paren->spatium());
103 25 : } else if (itemAddToSkyline) {
104 : // Space following item shape against this
105 25 : minDist = -HorizontalSpacing::minHorizontalDistance(paren->shape().translated(
106 : ldata->pos()), dummyItemShape, paren->spatium());
107 : }
108 50 : paren->mutldata()->moveX(minDist);
109 :
110 50 : return;
111 : }
112 :
113 : // 2 parentheses
114 239 : assert(leftParen && rightParen);
115 :
116 239 : if (!itemAddToSkyline) {
117 0 : return;
118 : }
119 :
120 239 : Parenthesis::LayoutData* leftLdata = leftParen->mutldata();
121 239 : Parenthesis::LayoutData* rightLdata = rightParen->mutldata();
122 :
123 239 : const double itemLeftX = dummyItemShape.bbox().x();
124 239 : const double parentWidth = dummyItemShape.bbox().width();
125 239 : const double itemRightX = itemLeftX + parentWidth;
126 :
127 239 : const double leftParenPadding = HorizontalSpacing::minHorizontalDistance(leftParen->shape().translated(leftLdata->pos()),
128 : dummyItemShape, leftParen->spatium());
129 239 : leftLdata->moveX(-leftParenPadding);
130 239 : dummyItemShape.add(leftParen->shape().translate(leftLdata->pos() + leftParen->staffOffset()));
131 :
132 239 : const double rightParenPadding = HorizontalSpacing::minHorizontalDistance(dummyItemShape, rightParen->shape().translated(
133 : rightLdata->pos()), rightParen->spatium());
134 239 : rightLdata->moveX(rightParenPadding);
135 :
136 : // If the right parenthesis has been padded against the left parenthesis, this means the parenthesis -> parenthesis padding distance
137 : // is larger than the width of the item the parentheses surrounds. In this case, the result is visually unbalanced. Move both parens
138 : // to the left (relative to the segment) in order to centre the item: (b ) -> ( b )
139 239 : const double itemWidth = parentWidth;
140 239 : double parenPadding = computeInternalParenthesisPadding(leftParen, rightParen);
141 :
142 239 : if (itemWidth >= parenPadding) {
143 238 : return;
144 : }
145 :
146 : // Move parentheses to place item in the middle
147 1 : const double leftParenX = leftLdata->pos().x() + leftParen->ldata()->bbox().x() + leftParen->ldata()->midPointThickness;
148 1 : const double rightParenX = rightLdata->pos().x() + rightParen->ldata()->bbox().x() + rightParen->width()
149 1 : - rightParen->ldata()->midPointThickness;
150 :
151 1 : const double leftParenToItem = itemLeftX - leftParenX;
152 1 : const double itemToRightParen = rightParenX - itemRightX;
153 1 : const double parenToItemDist = (leftParenToItem + itemToRightParen) / 2;
154 :
155 1 : leftLdata->moveX(-(parenToItemDist - leftParenToItem));
156 1 : rightLdata->moveX(-(itemToRightParen - parenToItemDist));
157 : }
158 :
159 528 : void ParenthesisLayout::layoutParenthesis(Parenthesis* item, Parenthesis::LayoutData* ldata, const LayoutContext& ctx)
160 : {
161 528 : setLayoutValues(item, ldata, ctx);
162 :
163 528 : if (ldata->symId == SymId::noSym) {
164 528 : createPathAndShape(item, ldata);
165 : } else {
166 0 : createSmuflShape(item, ldata);
167 : }
168 528 : }
169 :
170 72047 : bool ParenthesisLayout::isInternalParenPadding(const EngravingItem* item1, const EngravingItem* item2)
171 : {
172 72047 : const Chord* chord1 = toChord(item1->findAncestor(ElementType::CHORD));
173 72047 : const Chord* chord2 = toChord(item2->findAncestor(ElementType::CHORD));
174 72047 : const EngravingObject* parent1 = item1->ownershipParent();
175 72047 : const EngravingObject* parent2 = item2->ownershipParent();
176 :
177 113063 : bool internalPadding = (item1->isParenthesis() && parent1 == item2)
178 70844 : || (item2->isParenthesis() && parent2 == item1)
179 70146 : || parent1 == parent2
180 144094 : || (chord1 && chord2 && chord1 == chord2);
181 :
182 72047 : return internalPadding;
183 : }
184 :
185 61067 : double ParenthesisLayout::computeExternalParenthesisPadding(const EngravingItem* item1, const EngravingItem* item2)
186 : {
187 61067 : EngravingItem* parent = item1->isParenthesis() ? item1->ownershipParentItem() : item2->ownershipParentItem();
188 :
189 61067 : ElementType type1 = item1->type();
190 61067 : ElementType type2 = item2->type();
191 :
192 61067 : const ParenPaddingTable& paddingTable = parent->score()->paddingTables().parenthesisPaddingTableFor(parent);
193 :
194 61067 : double padding = paddingTable.padding(type1, type2);
195 :
196 61067 : double scaling = (item1->mag() + item2->mag()) / 2;
197 61067 : padding *= scaling;
198 61067 : return padding;
199 : }
200 :
201 72047 : double ParenthesisLayout::computeParenthesisPadding(const EngravingItem* item1, const EngravingItem* item2)
202 : {
203 72047 : if (isInternalParenPadding(item1, item2)) {
204 10980 : return computeInternalParenthesisPadding(item1, item2);
205 : }
206 :
207 61067 : return computeExternalParenthesisPadding(item1, item2);
208 : }
209 :
210 11219 : double ParenthesisLayout::computeInternalParenthesisPadding(const EngravingItem* item1, const EngravingItem* item2)
211 : {
212 11219 : bool parenFirst = item1->isParenthesis();
213 11219 : const EngravingItem* other = item1->isParenthesis() ? item2 : item1;
214 11219 : const double spatium = item1->style().spatium();
215 11219 : double padding = 0.0;
216 :
217 11219 : switch (other->type()) {
218 2988 : case ElementType::NOTE:
219 2988 : if (item1->staffType() && item1->staffType()->isTabStaff()) {
220 468 : padding = item1->style().styleS(Sid::tabFretPadding).val() * spatium;
221 : } else {
222 2520 : padding = 0.3 * spatium;
223 : }
224 2988 : break;
225 828 : case ElementType::ARTICULATION:
226 : case ElementType::ACCIDENTAL:
227 : case ElementType::STEM:
228 828 : padding = 0.3 * spatium;
229 828 : break;
230 18 : case ElementType::NOTEDOT:
231 18 : padding = 0.15 * spatium;
232 18 : break;
233 744 : case ElementType::LEDGER_LINE:
234 744 : padding = 0.3 * spatium;
235 744 : break;
236 0 : case ElementType::HOOK:
237 0 : padding = 0.3 * spatium;
238 0 : break;
239 566 : case ElementType::KEYSIG:
240 566 : padding = (parenFirst ? 0.35 : 0.25) * spatium;
241 566 : break;
242 542 : case ElementType::TIMESIG:
243 542 : padding = (parenFirst ? 0.2 : 0.25) * spatium;
244 542 : break;
245 729 : case ElementType::CLEF:
246 729 : padding = (parenFirst ? 0.2 : 0.25) * spatium;
247 729 : break;
248 64 : case ElementType::HARMONY:
249 64 : padding = 0.2 * spatium;
250 64 : break;
251 4700 : case ElementType::PARENTHESIS:
252 4700 : padding = spatium;
253 4700 : break;
254 40 : default:
255 40 : padding = 0.1 * spatium;
256 40 : break;
257 : }
258 :
259 11219 : double scaling = (item1->mag() + item2->mag()) / 2;
260 11219 : padding *= scaling;
261 11219 : return padding;
262 : }
263 :
264 592 : void ParenthesisLayout::createPathAndShape(Parenthesis* item, Parenthesis::LayoutData* ldata)
265 : {
266 592 : const double spatium = item->spatium();
267 592 : const bool leftBracket = item->direction() == DirectionH::LEFT;
268 :
269 592 : const double startY = ldata->startY;
270 592 : const double height = ldata->height;
271 592 : const double xPos = ldata->pos().x();
272 592 : const double mag = ldata->mag();
273 592 : const double maxMidPointThickness = MAX_MID_THICKNESS.toAbsolute(spatium);
274 592 : const double midPointThickness = std::min(ldata->midPointThickness.value(), maxMidPointThickness);
275 :
276 592 : const double heightInSpatium = height / spatium;
277 592 : const double shoulderYOffset = 0.2 * height;
278 :
279 592 : if (std::isinf(height)) {
280 0 : LOGE() << "Error: parenthesis height is infinite";
281 0 : return;
282 : }
283 :
284 : // Control width of parentheses. We don't want tall parens to be too wide, nor do we want parens at a small scale to lose their curve too much
285 592 : double shoulderX = !muse::RealIsNull(ldata->shoulderWidth()) ? ldata->shoulderWidth() : 0.2
286 520 : * std::pow(height, 0.95) * std::pow(mag, 0.1);
287 :
288 592 : const double minShoulderX = 0.25 * spatium * ldata->intrinsicMag();
289 592 : shoulderX = std::max(shoulderX, minShoulderX);
290 :
291 592 : PointF start = PointF(xPos, startY);
292 592 : const PointF end = PointF(xPos, start.y() + height);
293 592 : const PointF endNormalised = end - start;
294 :
295 592 : const int direction = leftBracket ? -1 : 1;
296 592 : const double shoulderForX = direction * shoulderX + midPointThickness * direction;
297 592 : const double shoulderBackX = direction * shoulderX + midPointThickness * direction * -1;
298 :
299 592 : const PointF bezier1for = PointF(shoulderForX, shoulderYOffset);
300 592 : const PointF bezier2for = PointF(shoulderForX, endNormalised.y() - shoulderYOffset);
301 592 : const PointF bezier1back = PointF(shoulderBackX, endNormalised.y() - shoulderYOffset);
302 592 : const PointF bezier2back = PointF(shoulderBackX, shoulderYOffset);
303 :
304 592 : PainterPath path = PainterPath();
305 592 : path.moveTo(PointF());
306 592 : path.cubicTo(bezier1for, bezier2for, endNormalised);
307 592 : path.cubicTo(bezier1back, bezier2back, PointF());
308 :
309 592 : ldata->path = path;
310 :
311 : // Fill shape
312 592 : Shape shape(Shape::Type::Composite);
313 :
314 592 : PointF startPoint = PointF();
315 592 : double midThickness = 2 * midPointThickness;
316 592 : int nbShapes = round(2.0 * heightInSpatium);
317 592 : nbShapes = std::clamp(nbShapes, 3, 10);
318 592 : PointF bezier1mid = bezier1for - PointF(midPointThickness * direction, 0.0);
319 592 : PointF bezier2mid = bezier2for - PointF(midPointThickness * direction, 0.0);
320 592 : const CubicBezier b(startPoint, bezier1mid, bezier2mid, endNormalised);
321 3044 : for (int i = 1; i <= nbShapes; i++) {
322 2452 : double percent = pow(sin(0.5 * M_PI * (double(i) / double(nbShapes))), 2);
323 2452 : const PointF point = b.pointAtPercent(percent);
324 2452 : RectF re = RectF(startPoint, point).normalized();
325 2452 : double approxThicknessAtPercent = (1 - 2 * std::abs(0.5 - percent)) * midThickness;
326 2452 : if (re.width() < approxThicknessAtPercent) {
327 672 : double adjust = (approxThicknessAtPercent - re.width()) * .5;
328 672 : re.adjust(-adjust, 0.0, adjust, 0.0);
329 : }
330 2452 : shape.add(re, item);
331 2452 : startPoint = point;
332 : }
333 :
334 592 : ldata->setShape(shape);
335 :
336 592 : item->setPos(start);
337 592 : }
338 :
339 0 : void ParenthesisLayout::createSmuflShape(Parenthesis* item, Parenthesis::LayoutData* ldata)
340 : {
341 0 : Shape bbox = Shape(item->symBbox(ldata->symId), item);
342 0 : double scale = item->ldata()->symScale;
343 0 : bbox.scale(SizeF(scale, scale));
344 0 : ldata->setShape(bbox);
345 0 : }
346 :
347 528 : void ParenthesisLayout::setLayoutValues(Parenthesis* item, Parenthesis::LayoutData* ldata, const LayoutContext& ctx)
348 : {
349 528 : if (!item->ownershipParent()) {
350 0 : return;
351 : }
352 :
353 528 : ldata->setPos(PointF());
354 528 : ldata->reset();
355 528 : ldata->path.reset();
356 528 : ldata->symId.reset();
357 :
358 : // Set ldata values based on parent
359 528 : switch (item->ownershipParent()->type()) {
360 438 : case ElementType::CHORD:
361 438 : setChordValues(item, ldata);
362 438 : break;
363 : // height & startY are set in MeasureLayout for clef, timesig and keysig
364 : // TODO: create generic way of matching height & startY between parentheses on different items
365 39 : case ElementType::CLEF:
366 39 : setClefValues(item, ldata);
367 39 : break;
368 33 : case ElementType::TIMESIG:
369 33 : setTimeSigValues(item, ldata, ctx);
370 33 : break;
371 10 : case ElementType::KEYSIG:
372 10 : break;
373 8 : case ElementType::HARMONY:
374 8 : setHarmonyValues(item, ldata, ctx);
375 8 : break;
376 0 : default:
377 0 : setDefaultValues(item, ldata);
378 0 : break;
379 : }
380 : }
381 :
382 39 : void ParenthesisLayout::setClefValues(Parenthesis* item, Parenthesis::LayoutData* ldata)
383 : {
384 39 : const EngravingItem* parent = item->ownershipParentItem();
385 39 : const Fraction tick = parent->tick();
386 39 : const Measure* measure = item->findMeasure();
387 :
388 39 : double offset = -parent->pos().y();
389 :
390 39 : if (!measure || tick != measure->endTick()) {
391 19 : ldata->startY.mut_value() += offset;
392 19 : return;
393 : }
394 :
395 20 : const double spatium = item->spatium();
396 20 : const Staff* staff = parent->staff();
397 20 : const Fraction tickPrev = tick - Fraction::eps();
398 20 : const StaffType* st = staff->staffType(tick);
399 20 : const StaffType* stPrev = !tickPrev.negative() ? item->staff()->staffType(tickPrev) : nullptr;
400 :
401 20 : offset += (st->yoffset().val() - (stPrev ? stPrev->yoffset().val() : 0)) * spatium;
402 20 : ldata->startY.mut_value() += offset;
403 : }
404 :
405 33 : void ParenthesisLayout::setTimeSigValues(Parenthesis* item, Parenthesis::LayoutData* ldata, const LayoutContext& ctx)
406 : {
407 33 : const TimeSig* parentTs = toTimeSig(item->ownershipParent());
408 33 : if (ctx.conf().styleV(Sid::timeSigPlacement).value<TimeSigPlacement>() == TimeSigPlacement::NORMAL) {
409 33 : return;
410 : }
411 :
412 0 : double yOffset = -parentTs->pos().y();
413 0 : ldata->startY.mut_value() += yOffset;
414 : }
415 :
416 438 : void ParenthesisLayout::setChordValues(Parenthesis* item, Parenthesis::LayoutData* ldata)
417 : {
418 438 : Chord* chord = toChord(item->ownershipParent());
419 :
420 438 : ldata->setMag(chord->mag());
421 438 : ldata->intrinsicMag = chord->intrinsicMag(); // Scaling information not linked to staff size. item->spatium is NOT scaled by this
422 :
423 438 : Shape notesShape;
424 :
425 438 : const NoteParenthesisInfo* parenInfo = chord->findNoteParenthesisInfo(item);
426 438 : IF_ASSERT_FAILED(parenInfo) {
427 0 : LOGD() << "No parenthesis info found for this chord";
428 0 : return;
429 : }
430 :
431 438 : const std::vector<Note*>& notes = parenInfo->notes();
432 :
433 438 : assert(!notes.empty());
434 :
435 986 : for (const Note* note : notes) {
436 548 : notesShape.add(getNoteShape(note, item).translated(note->pos()));
437 : }
438 :
439 438 : const StaffType* st = chord->staffType();
440 438 : const bool isTab = st->isTabStaff();
441 :
442 438 : auto calculateMidPointThickness = [item, ldata, isTab]() -> double {
443 438 : const double sp = item->spatium();
444 438 : const double sp0 = item->defaultSpatium();
445 :
446 438 : IF_ASSERT_FAILED(sp > 0.0 && sp0 > 0.0) {
447 0 : LOGE() << "spatium or default spatium is 0";
448 0 : return 0.0;
449 : }
450 :
451 438 : const double exponent = isTab ? 0.36 : 0.33;
452 438 : const double exponentComplement = 1.0 - exponent;
453 :
454 438 : const double normalizedHeight = ldata->height / sp;
455 :
456 : // Scale factor that keeps the normalized formula compatible with the
457 : // original formula at the default spatium.
458 438 : const double scaleNormalization = sp / std::pow(sp0, exponentComplement);
459 438 : const double normalizedThickness = std::pow(normalizedHeight, exponent)
460 438 : * scaleNormalization
461 438 : * ldata->intrinsicMag();
462 :
463 438 : return normalizedThickness;
464 438 : };
465 :
466 438 : if (isTab) {
467 26 : ldata->startY = notesShape.top();
468 26 : ldata->height = notesShape.bbox().height();
469 : } else {
470 412 : ldata->startY = notesShape.top() - 0.25 * item->spatium() * ldata->intrinsicMag();
471 412 : ldata->height = notesShape.bbox().height() + 0.5 * item->spatium() * ldata->intrinsicMag();
472 : }
473 438 : ldata->midPointThickness.set_value(calculateMidPointThickness());
474 438 : ldata->endPointThickness.set_value(0.05);
475 438 : }
476 :
477 8 : void ParenthesisLayout::setHarmonyValues(Parenthesis* item, Parenthesis::LayoutData* ldata, const LayoutContext& ctx)
478 : {
479 8 : const double spatium = item->spatium();
480 8 : Harmony* parent = toHarmony(item->ownershipParent());
481 8 : RectF bbox = parent->ldata()->bbox();
482 :
483 8 : double endPointThickness = 0.03;
484 8 : double extension = 0.1 * spatium * (parent->size() / 10.0);
485 8 : double bottom = parent->baseLine() + extension;
486 :
487 8 : double defaultCapHeight = muse::draw::FontMetrics::capHeight(parent->font());
488 :
489 8 : double topCapHeight = DBL_MAX;
490 8 : const TextSegment* rootTextSeg = nullptr;
491 8 : if (!parent->ldata()->renderItemList.value().empty()) {
492 48 : for (const HarmonyRenderItem* renderItem : parent->ldata()->renderItemList.value()) {
493 40 : if (const TextSegment* ts = dynamic_cast<const TextSegment*>(renderItem)) {
494 40 : topCapHeight = std::min(topCapHeight, ts->pos().y() - ts->capHeight());
495 :
496 40 : rootTextSeg = !rootTextSeg ? ts : rootTextSeg;
497 : }
498 : }
499 : } else {
500 0 : topCapHeight = defaultCapHeight;
501 : }
502 :
503 8 : double top = topCapHeight - extension;
504 8 : double height = bottom - top;
505 :
506 8 : if (ctx.conf().styleB(Sid::harmonyParenUseSmuflSym)) {
507 0 : ldata->symId = item->direction() == DirectionH::LEFT ? SymId::csymParensLeftTall : SymId::csymParensRightTall;
508 0 : double symHeight = item->symHeight(ldata->symId);
509 0 : ldata->symScale = height / symHeight;
510 :
511 0 : return;
512 : }
513 :
514 8 : double rootCapHeight = rootTextSeg ? rootTextSeg->capHeight() : defaultCapHeight;
515 8 : double scale = (height - 2 * extension) / rootCapHeight;
516 :
517 8 : ldata->setMag(parent->mag());
518 8 : ldata->startY = top;
519 8 : ldata->height = height;
520 : static constexpr double HEIGHT_TO_WIDTH_RATIO = 20;
521 8 : ldata->midPointThickness.set_value(ldata->height / HEIGHT_TO_WIDTH_RATIO * ldata->mag() * 1 / std::sqrt(scale));
522 8 : ldata->endPointThickness.set_value(endPointThickness);
523 :
524 8 : double shoulder = 0.2 * ldata->height * std::pow(ldata->mag(), 0.1) * 1 / std::sqrt(scale);
525 8 : ldata->shoulderWidth = shoulder;
526 :
527 8 : const double PADDING = spatium * 0.2;
528 8 : ldata->setPosX(item->direction() == DirectionH::RIGHT ? bbox.right() + PADDING : bbox.left() - PADDING);
529 : }
530 :
531 0 : void ParenthesisLayout::setDefaultValues(Parenthesis* item, Parenthesis::LayoutData* ldata)
532 : {
533 0 : const double spatium = item->spatium();
534 0 : EngravingItem* parent = item->layoutParent();
535 0 : RectF bbox = parent->ldata()->bbox();
536 :
537 0 : ldata->setMag(parent->mag());
538 0 : ldata->startY = bbox.top() - 0.25 * spatium;
539 0 : ldata->height = bbox.height() + 0.5 * spatium;
540 0 : ldata->midPointThickness.set_value(ldata->height / 60 * ldata->mag()); // 0.1sp for a height of 6sp
541 0 : const double PADDING = spatium * 0.2;
542 0 : ldata->setPosX(item->direction() == DirectionH::RIGHT ? bbox.right() + PADDING : bbox.left() - PADDING);
543 0 : }
544 :
545 289 : Shape ParenthesisLayout::getParentShape(const EngravingItem* parent)
546 : {
547 289 : Shape parentShape = parent->shape();
548 :
549 289 : bool isChord = parent->isChord();
550 :
551 289 : parentShape.remove_if([isChord](ShapeElement& s) {
552 6364 : return !s.item() || s.item()->isParenthesis()
553 863 : || (s.item()->isLaissezVibSegment() && isChord)
554 863 : || (s.item()->isHook() && isChord)
555 859 : || (s.item()->isStem() && isChord)
556 747 : || (s.item()->isArpeggio() && isChord)
557 6364 : || (s.item()->isChordBracket() && isChord);
558 : });
559 :
560 289 : return parentShape;
561 0 : }
562 :
563 548 : Shape ParenthesisLayout::getNoteShape(const Note* note, Parenthesis* paren)
564 : {
565 548 : IF_ASSERT_FAILED(note && paren) {
566 0 : return Shape();
567 : }
568 548 : Shape noteShape = note->shape();
569 548 : noteShape.remove_if([paren](ShapeElement& s) {
570 812 : return s.item() == paren || s.item()->isBend() || s.item()->isParenthesis() || s.item()->isAccidental() || s.item()->isNoteDot() || s.item()->isLaissezVibSegment();
571 : });
572 :
573 548 : return noteShape;
574 548 : }
|