// (JB) \bug There is probably a bug somewhere: with map mode // different than "ISOTROPIC" (proportionnal), beamings polygons between "UP" stems // draw at an incorrect x position, while "DOWN" beams are always ok. // // Too long, needs some code-factorization, if possible. void GRBeam::tellPosition( GObject * gobj, const NVPoint & p_pos) { /* Beams are polygons made of four points: 0 2 ------------------ | | ------------------ 1 3 */ // a new test is performed: if it is a systemTag and // it is not a systemcall (checkpos), than we can just do nothing. if (error || !mAssociated || ( mAssociated->GetCount() == 0 ) || ( tagtype == GRTag::SYSTEMTAG && !mIsSystemCall )) return; GRNotationElement * el = dynamic_cast<GRNotationElement *>(gobj); if (!el || !el->getGRStaff()) return; GRSystemStartEndStruct * sse = getSystemStartEndStruct(el->getGRStaff()->getGRSystem()); assert(sse); if (el != sse->endElement) return; GRBeamSaveStruct * st = (GRBeamSaveStruct *)sse->p; GuidoPos pos; GREvent * startEl = GREvent::cast(sse->startElement); GREvent * endEl = GREvent::cast(sse->endElement); // this is the staff to which the beam belongs and who draws it. const GRStaff * beamstaff = sse->startElement->getGRStaff(); PosInfos infos = { dirUP, LSPACE, 1.0f, (endEl == startEl)}; ARBeam * arBeam = getARBeam(); const bool isSpecBeam = arBeam->isGuidoSpecBeam(); if(startEl) infos.stemdir = startEl->getStemDirection(); else if(endEl) infos.stemdir = endEl->getStemDirection(); if(level != 0) return; NVPoint offset = initp0 (sse, startEl, infos); initp1 (sse, infos); offset = initp2 (sse, endEl, infos); initp3 (sse, infos); // ----------------------------------------------------------- // Now, we adjust the stemlengths, according to beamslope ... // we have the start and end-position in st->p // We have to determine the slope and adjust the slope to minimum and maximum ... // ----------------------------------------------------------- float stemWidth = st->p[2].x - st->p[0].x; float slope = (st->p[2].y - st->p[0].y) / stemWidth; // another hack to control the slope when events are on different staves - DF sept 15 2009 if (startEl && endEl && (startEl->getGRStaff() != endEl->getGRStaff())) { while ((slope < -0.20) || (slope > 0.20)) { float shift = (slope < 0) ? -LSPACE/4 : LSPACE/4; st->p[0].y += shift; st->p[1].y += shift; st->p[2].y -= shift; st->p[3].y -= shift; slope = (st->p[2].y - st->p[0].y) / stemWidth; } } bool needsadjust = true; // we have to adjust the slope ONLY if the stemlength // of the first and last element has not been set automatically! // and if we are note in the case of a chained feather beam if ( (startEl && startEl->getStemLengthSet() && endEl && endEl->getStemLengthSet()) || tagtype == SYSTEMTAG || (arBeam && isSpecBeam) || (isFeathered && startEl && startEl->stemHasBeenChanged())) { needsadjust = false; } else slopeAdjust (sse, startEl, endEl, slope, infos); if (arBeam && isSpecBeam) { // then we have to set the length ... if it is not set otherwise ... GRSingleNote * nt = dynamic_cast<GRSingleNote *>(sse->startElement); if(nt && !nt->getStemLengthSet()) { const float myval = arBeam->dy1->getValue(); nt->setStemLength( myval); } nt = dynamic_cast<GRSingleNote *>(sse->endElement); if(nt && !nt->getStemLengthSet()) { float myval; if (arBeam->dy3 && arBeam->dy3->TagIsSet()) myval = arBeam->dy3->getValue(); else myval = arBeam->dy1->getValue(); nt->setStemLength( myval); } } float offsetbeam = 0; // nobeamoffset describes, if no beamoffset is valid: if notes ask for different beam-offsets // then, there is just no offset .... things must be changed manually then .... bool nobeamoffset = false; if(( startEl && startEl->getStemLengthSet()) || ( endEl && endEl->getStemLengthSet())) nobeamoffset = true; for (int counter = 0; counter < 2; ++counter ) { if (counter == 1) { if ((offsetbeam == 0) || nobeamoffset) break; else if (!nobeamoffset) { st->p[0].y -= offsetbeam; st->p[1].y -= offsetbeam; st->p[2].y -= offsetbeam; st->p[3].y -= offsetbeam; offsetbeam = 0; } } pos = sse->startpos; while (pos) { // now we calculate the stem-end-positions ... GuidoPos oldpos = pos; GREvent * sn = GREvent::cast(mAssociated->GetNext(pos)); if (sn) { float rx = sn->getStemStartPos().x - st->p[0].x; if (tagtype == SYSTEMTAG) rx += sn->getGRStaff()->getPosition().x; float disty = st->p[2].y - st->p[0].y; float distx = st->p[2].x - st->p[0].x; float ly = disty / distx * rx; ly += st->p[0].y; float diffy = (float)sn->getStemStartPos().y; GDirection adjustdir=dirAUTO; if (sn->getStemDirection() == dirUP) { diffy += (float)sn->getStemLength(); adjustdir = dirUP; } else if (sn->getStemDirection() == dirDOWN) { adjustdir = dirDOWN; diffy -= (float)sn->getStemLength(); } ly -= diffy; if (tagtype == SYSTEMTAG) ly -= (float)sn->getGRStaff()->getPosition().y; // if we have a beam between grace notes, we don't want an offbase that whould make the stems too long GRNote * gnote = dynamic_cast<GRNote*>(startEl); bool isGrace = gnote ? gnote->isGraceNote() : false; float offbase = isGrace ? 0 : 3.5f * infos.currentLSPACE; if (ly < 0) { if (needsadjust) { if (adjustdir == dirDOWN) { const float newoffs = ly - offbase; if (newoffs < offsetbeam) { if (offsetbeam > 0) { GuidoTrace("WARNING: different beam adjustments!"); nobeamoffset = true; } else offsetbeam = newoffs; } ly = -offbase; } else if (ly > -offbase) { const float newoffs = ly + offbase; if (newoffs > offsetbeam) { if (offsetbeam < 0) { GuidoTrace("WARNING: different beam adjustments!"); nobeamoffset = true; } else offsetbeam = newoffs; } } } ly = -ly; } else if (needsadjust) { if (adjustdir == dirUP) { const float newoffs = ly + offbase; if (newoffs > offsetbeam) { if (offsetbeam < 0) { GuidoTrace("WARNING: different beam adjustments!"); nobeamoffset = true; } else offsetbeam = newoffs; } ly = offbase; } else if (ly < offbase) { const float newoffs = ly - offbase; if (newoffs < offsetbeam) { if (offsetbeam > 0) { GuidoTrace("WARNING: different beam adjustments!"); nobeamoffset = true; } else offsetbeam = newoffs; } } } // sn->changeStemLength( ly ); // adjusted - DF sept 15 2009 sn->changeStemLength( ly - infos.currentLSPACE/20 ); // so that the possible next featherd beam knows that he is chained // (and musn't change its slope) sn->setStemChanged(); } if (oldpos == sse->endpos) break; } //endEl->setStemChanged(); } if(!smallerBeams.empty()) { for(std::vector<GRBeam *>::iterator it = smallerBeams.begin(); it < smallerBeams.end(); it++) { (*it)->decLevel(); (*it)->tellPosition((*it)->getEndElement(), (*it)->getEndElement()->getPosition()); } return; } // -- Now we need to add the simplebeams as simplebeamgroups ... NVPoint myp[4]; int dir = st->direction; if (st->dirset) { // then the direction was set explicitly by the user ... GREvent * sn = GREvent::cast(mAssociated->GetHead()); if (sn) dir = sn->getStemDirection(); } else dir = infos.stemdir; bool first = true; pos = sse->startpos; // - These constants define the space and the thickness of additionnal beams. const float yFact1 = 0.75f * infos.currentLSPACE; // was 0.7f const float yFact2 = 0.4f * infos.currentLSPACE; // if we have a feathered beam, we just have to draw the main beam (already done) // and the other simple beams will only depend on the begining and ending // points, regardless of the durations of the inner notes. if(isFeathered) { ARFeatheredBeam * ar = static_cast<ARFeatheredBeam *>(getARBeam()->isARFeatheredBeam()); int begin = 0; int end = 0; GREvent * stemNoteBegin = GREvent::cast(mAssociated->GetHead()); GDirection localDir = stemNoteBegin->getStemDirection(); float yLocalFact1 = yFact1 * localDir * infos.currentSize; float yLocalFact2 = yFact2 * localDir * infos.currentSize; // if the user hasn't set the durations as parameters, // we will take the first and last notes'durations if(!ar->isDurationsSet()) { ar->findDefaultPoints(); } end = ar->getLastBeaming(); begin = ar->getFirstBeaming(); for(int i=1;i<=begin; i++) { myp[0] = st->p[0]; myp[0].y += (i-1) * yLocalFact1; myp[1].x = myp[0].x; myp[1].y = myp[0].y + yLocalFact2; myp[2] = st->p[2]; if(end>i ||(end==i && i!=1)) // no need to draw the main beam again. myp[2].y += (i-1) * yLocalFact1; else myp[2].y += (end-1) * yLocalFact1; myp[3].x = myp[2].x; myp[3].y = myp[2].y + yLocalFact2; GRSimpleBeam * tmpbeam = new GRSimpleBeam(this,myp); if( st->simpleBeams == 0 ) st->simpleBeams = new SimpleBeamList(1); st->simpleBeams->AddTail(tmpbeam); } // if end > begin for(int i=begin; i<end; i++) { myp[0] = st->p[0]; myp[0].y += (begin-1) * yLocalFact1; myp[1].x = myp[0].x; myp[1].y = myp[0].y + yLocalFact2; myp[2] = st->p[2]; myp[2].y += i * yLocalFact1; myp[3].x = myp[2].x; myp[3].y = myp[2].y + yLocalFact2; GRSimpleBeam * tmpbeam = new GRSimpleBeam(this,myp); if( st->simpleBeams == 0 ) st->simpleBeams = new SimpleBeamList(1); st->simpleBeams->AddTail(tmpbeam); } // in order to draw the total duration of the beam if(drawDur) { TYPE_TIMEPOSITION begin = ar->getBeginTimePosition(); TYPE_TIMEPOSITION end = ar->getEndTimePosition(); TYPE_DURATION dur = end - begin; int num = dur.getNumerator(); int den = dur.getDenominator(); stringstream out; out << num << '/' << den; st->duration = out.str(); size_t n = st->duration.length(); GREvent * ev = dynamic_cast<GREvent *>(mAssociated->GetHead()); const NVPoint p1 = ev->getStemEndPos(); float xBegin = ev->getPosition().x; ev = dynamic_cast<GREvent *>(mAssociated->GetTail()); const NVPoint p2 = ev->getStemEndPos(); float xEnd = ev->getPosition().x + ev->getBoundingBox().Width()/2; int dir = ev->getStemDirection(); float Y1; float Y2; if(dir>0) { Y1 = min(p1.y, p2.y) - LSPACE; if(Y1>=getLastPositionOfBarDuration().first && Y1<getLastPositionOfBarDuration().second+LSPACE/2) Y1 -= LSPACE; Y2 = Y1 - LSPACE/2; getLastPositionOfBarDuration().first = Y2; getLastPositionOfBarDuration().second = Y1; } else { Y1 = max(p1.y, p2.y) + LSPACE; if(Y1>=getLastPositionOfBarDuration().first-LSPACE/2 && Y1<getLastPositionOfBarDuration().second) Y1 += LSPACE; Y2 = Y1 + LSPACE/2; getLastPositionOfBarDuration().first = Y1; getLastPositionOfBarDuration().second = Y2; } if (xBegin > xEnd) { if (sse->endflag == GRSystemStartEndStruct::OPENRIGHT) xEnd = sse->endElement->getPosition().x; if (sse->startflag == GRSystemStartEndStruct::OPENLEFT) xBegin = sse->startElement->getPosition().x; } float x = xBegin + (xEnd - xBegin) / 2; float X1 = x - (n - 0.5f) / 2 * LSPACE; float X2 = x + (n - 0.5f) / 2 * LSPACE; st->DurationLine[0] = NVPoint(xBegin, Y1); st->DurationLine[1] = NVPoint(xBegin, Y2); st->DurationLine[2] = NVPoint(X1, Y2); st->DurationLine[3] = NVPoint(X2, Y2); st->DurationLine[4] = NVPoint(xEnd, Y2); st->DurationLine[5] = NVPoint(xEnd, Y1); } } // for beam length adjustment - DF sept 15 2009 const float xadjust = infos.currentLSPACE/10; GDirection lastLocalDir = dirOFF; int previousBeamsCount = 0; while (pos && !isFeathered) { GuidoPos oldpos = pos; GREvent * stemNote = GREvent::cast(mAssociated->GetNext(pos)); if (stemNote) { GDirection localDir = stemNote->getStemDirection(); float yLocalFact1 = yFact1 * localDir * infos.currentSize; float yLocalFact2 = yFact2 * localDir * infos.currentSize; // now we check the number of beams ... if (stemNote->getBeamCount() < stemNote->getNumFaehnchen()) { float beamCount = (float)(stemNote->getBeamCount()); stemNote->incBeamCount(); if (first && (sse->startflag == GRSystemStartEndStruct::OPENLEFT)) { // the additional beam starts at the startElement (glue), we have more beams to draw myp[0] = sse->startElement->getPosition(); if (tagtype == SYSTEMTAG) myp[0] += stemNote->getGRStaff()->getPosition(); myp[1] = myp[0]; // first = false; // never read (according to clang :-) } else { // the additional beam starts at sn. We have more beams to draw myp[0] = stemNote->getStemStartPos(); if (tagtype == SYSTEMTAG) myp[0] += stemNote->getGRStaff()->getPosition(); myp[0].y += beamCount * yLocalFact1; if (localDir != dir) myp[0].y -= yLocalFact2; myp[1].x = myp[0].x; myp[1].y = myp[0].y + yLocalFact2; } // now we look for the endposition GREvent * sn2 = NULL; GuidoPos tmppos = pos; // partialbeam is set, if the new SimpleBeam only covers part of the masterBeam. int partialbeam = 0; while (tmppos) { GuidoPos oldpos2 = tmppos; GREvent * tmpsn = GREvent::cast(mAssociated->GetNext(tmppos)); if (tmpsn) { if (tmpsn->getBeamCount() < tmpsn->getNumFaehnchen()) { sn2 = tmpsn; sn2->incBeamCount(); continue; } else { partialbeam = 1; break; } } if (oldpos2 == sse->endpos) break; } if (sn2) { if (!partialbeam && sse->endflag == GRSystemStartEndStruct::OPENRIGHT) { // then the position is different ... myp[2] = sse->endElement->getPosition(); myp[2].x += xadjust; if (tagtype == SYSTEMTAG) myp[2] += sn2->getGRStaff()->getPosition(); myp[2].y = myp[0].y; } else { // we have an End-Position ... myp[2] = sn2->getStemEndPos(); myp[2].x += xadjust; if (tagtype == SYSTEMTAG) myp[2] += sn2->getGRStaff()->getPosition(); myp[2].y += beamCount * yLocalFact1; } if (localDir != dir) myp[2].y -= yLocalFact2; myp[3].x = myp[2].x; myp[3].y = myp[2].y + yLocalFact2; } else { // we do not have an End-Positon single beam ... (meaning a single straight flag) // but only, if it is not open on the left or the right hand side. const float slope = (float)(st->p[2].y - st->p[0].y ) / (float)(st->p[2].x - st->p[0].x); if (sse->startflag == GRSystemStartEndStruct::OPENLEFT) { // then we have to deal with the startposition of the glue-element .... // BUT, you can only set this, if the previous beam had this beamcount at the end .... // how do I know that? not now. // sn is the only element .... and we are open on the left ... if (tagtype == SYSTEMTAG) myp[0] += stemNote->getGRStaff()->getPosition(); myp[1] = myp[0]; myp[2] = stemNote->getStemEndPos(); myp[2].x += xadjust; if (tagtype == SYSTEMTAG) myp[2] += stemNote->getGRStaff()->getPosition(); myp[2].y += beamCount * yLocalFact1; if (localDir != dir) myp[2].y -= yLocalFact2; myp[3] = myp[2]; myp[3].y += yLocalFact2; } else if (sse->endflag == GRSystemStartEndStruct::OPENRIGHT) { myp[0] = stemNote->getStemEndPos(); if (tagtype == SYSTEMTAG) myp[0] += stemNote->getGRStaff()->getPosition(); myp[0].y += beamCount * yLocalFact1; if (localDir != dir) myp[0].y -= yLocalFact2; myp[1] = myp[0]; myp[1].y += yLocalFact2; myp[2] = sse->endElement->getPosition(); myp[2].x += xadjust; if (tagtype == SYSTEMTAG) myp[2] += sn2->getGRStaff()->getPosition(); myp[2].y = myp[0].y; myp[3].x = myp[2].x; myp[3].y = myp[1].y; } /* 26/11/03 Beaming bug: wrong direction for partial beam (beam-bug.gmn) can be tested but changing this test. startpos check added to correct problem with partial beam going outside a group like in [ _/16 c d/8 ] */ else if( oldpos == sse->endpos || pos == NULL || ((!stemNote->isSyncopated()) && (oldpos != sse->startpos))) { // Partial beams leftward ( using slope) myp[2] = stemNote->getStemEndPos(); myp[2].x += xadjust; if (tagtype == SYSTEMTAG) myp[2] += stemNote->getGRStaff()->getPosition(); if (localDir != dir) myp[2].y -= yLocalFact2; myp[2].y += beamCount * yLocalFact1; myp[3] = myp[2]; myp[3].y += yLocalFact2; myp[0] = myp[2]; myp[0].x -= infos.currentLSPACE; myp[0].y -= slope * infos.currentLSPACE; myp[1] = myp[0]; myp[1].y += yLocalFact2; // ? We are at the end, there is no valid event at the end ... so what do we do? // useless tests: the code was the same and is now outside the test (above) - DF sept 15 2009 /* if (sse->endflag == GRSystemStartEndStruct::OPENRIGHT) { } else { } */ } else { // Partial beams rightward ( using slope) myp[2] = myp[0]; myp[2].x += infos.currentLSPACE; myp[2].y += slope * infos.currentLSPACE; myp[3] = myp[2]; myp[3].y += yLocalFact2; } } // now we construct a SimpleBeam, we now have to "undo" the systemTag-stuff if (tagtype == SYSTEMTAG) { const NVPoint & offset = beamstaff->getPosition(); myp[0] -= offset; myp[1] -= offset; myp[2] -= offset; myp[3] -= offset; } if (sse->startflag == GRSystemStartEndStruct::OPENLEFT) { myp[0].y = myp[2].y; myp[1].y = myp[3].y; } GRSimpleBeam * tmpbeam = new GRSimpleBeam(this,myp); if( st->simpleBeams == 0 ) st->simpleBeams = new SimpleBeamList(1); st->simpleBeams->AddTail(tmpbeam); pos = sse->startpos; oldpos = pos; first = true; lastLocalDir = localDir; previousBeamsCount = stemNote->getBeamCount() - 1; } // a new hack, again to catch stems directions change - DF sept 15 2009 else if (localDir != dir) { // check for stems length NVPoint stemloc = stemNote->getStemStartPos(); if (tagtype == SYSTEMTAG) stemloc += stemNote->getGRStaff()->getPosition(); int beamscount = stemNote->getBeamCount() - 1; if ((beamscount > 0) && (previousBeamsCount > beamscount) && (lastLocalDir != localDir)) { if (localDir == dirUP) stemNote->changeStemLength(stemNote->getStemLength() + (yLocalFact1 * beamscount)); else if (localDir == dirDOWN) stemNote->changeStemLength(stemNote->getStemLength() - (yLocalFact1 * beamscount)); } } } if (oldpos == sse->endpos) break; } GuidoPos stemPos = sse->startpos; while(stemPos) { GREvent * stemNote = GREvent::cast(mAssociated->GetNext(stemPos)); if(stemNote) { GuidoPos tagpos = NULL; if (stemNote->getAssociations()) tagpos = stemNote->getAssociations()->GetHeadPosition(); while(tagpos) { GRNotationElement * tag = stemNote->getAssociations()->GetNext(tagpos); GRTremolo * trem = dynamic_cast<GRTremolo*>(tag); if(trem) { trem->tellPosition(stemNote,stemNote->getPosition()); if(trem->isTwoNotesTremolo()) // in order to force the second pitch (especially the chords) to update { GREvent * secondPitch = dynamic_cast<GREvent*>(mAssociated->GetNext(stemPos)); if(secondPitch) trem->tellPosition(secondPitch, secondPitch->getPosition()); } } } } } // now we have to make sure, that the original positions // for the beam are set for the right staff if (tagtype == SYSTEMTAG) { const NVPoint &offset = beamstaff->getPosition(); st->p[0] -= offset; st->p[1] -= offset; st->p[2] -= offset; st->p[3] -= offset; } }
/** Places the tuplet bracket and/or numeral, close to its group of notes. Calculates the positions of the two possible tuplet bracket (above and below) then choose the best one. */ void GRTuplet::automaticPosition(GObject * caller, const NVPoint & inPos ) { GREvent * callerEv = GREvent::cast( caller ); if( callerEv == 0 ) return; GRStaff * staff = callerEv->getGRStaff(); if( staff == 0 ) return; GRSystemStartEndStruct * sse = getSystemStartEndStruct(staff->getGRSystem()); if( sse == 0 ) return; GRTupletSaveStruct * st = (GRTupletSaveStruct *)sse->p; if( st == 0 ) return; GREvent * startElement = GREvent::cast( sse->startElement ); if( startElement == 0 ) return; GREvent * endElement = GREvent::cast( sse->endElement ); if( endElement == 0 ) return; // - Accept being positioned only once, if possible by the last tuplet element. //if(( callerEv != endElement ) && ((callerEv != startElement) || (endElement != 0))) if( callerEv != endElement ) return; // - Check for beams const bool firstBeamed = (startElement->getBeamCount() > 0); const bool lastBeamed = (endElement->getBeamCount() > 0); mShowLeftBrace = !(firstBeamed && lastBeamed ); mShowRightBrace = mShowLeftBrace; // - Get first and last element positions to work with float startX, endX; endX = 0; float startUpY, startDownY, endUpY, endDownY; // x positions const float halfNoteWidth = LSPACE * float(0.65); // harcoded startX = startElement->getPosition().x; endX = endElement->getPosition().x; // if( endX == startX ) return; // DF commented: results in strange vertical bars startX -= halfNoteWidth; endX += halfNoteWidth; // y positions const NVRect & leftBox = startElement->getBoundingBox(); const NVRect & rightBox = endElement->getBoundingBox(); const float elPos1y = startElement->getPosition().y; const float elPos2y = endElement->getPosition().y; startUpY = leftBox.top + elPos1y; endUpY = rightBox.top + elPos2y; startDownY = leftBox.bottom + elPos1y; endDownY = rightBox.bottom + elPos2y; // - Calculate a virtual line from the first to the last element, int stemStats = 0; const float deltaX = (endX - startX); float slopeUp = (endUpY - startUpY) / deltaX; // slope of the virtal line above float slopeDown = (endDownY - startDownY) / deltaX; // slope of the virtal line below float x, yUp, yDown, distUp, distDown; float mxUp = 0, myUp = 0, mxDown = 0, myDown = 0; // 'extreme' middle point bool collideUp = false; bool collideDown = false; GuidoPos pos = mAssociated->GetHeadPosition(); while( pos ) { GREvent * el = GREvent::cast( mAssociated->GetNext(pos)); if( el == 0 ) continue; // - Generate stats about stem directions GDirection stemDir = el->getStemDirection(); if( stemDir == dirUP ) ++ stemStats; else if( stemDir == dirDOWN ) -- stemStats; if(( el != startElement ) && ( el != endElement )) { // - Get the element box const NVRect & elBox = el->getBoundingBox(); const NVPoint & elPos = el->getPosition(); x = elPos.x; yUp = elBox.top + elPos.y; yDown = elBox.bottom + elPos.y; // - Calculate the y-distance between the element and the virtual line. // distY = startY - (y - slope * (x - startX)); distUp = (startUpY - yUp) + (x - startX) * slopeUp; distDown = (startDownY - yDown) + (x - startX) * slopeDown; if( distUp > 0 ) // then the point is above the virtual line { mxUp = x; myUp = yUp; collideUp = true; //startUpY -= distUp; // TODO: better handling of this collision //endUpY -= distUp; } if( distDown < 0 ) // then the point is below the virtual line { mxDown = x; myDown = yDown; collideDown = true; //startDownY -= distDown; //endDownY -= distDown; } } } // - Adjust the brace to avoid collisions. It must be above (or below) all elements, // while remaining close to those elements, and avoid being in the staff. // brace 1 (upward) if( collideUp ) { if(( myUp <= startUpY ) && ( myUp <= endUpY )) // middle point above start and end points { // slopeUp = 0; // horizontal startUpY = myUp; endUpY = myUp; } else { if( myUp <= endUpY ) // middle point above end point only: shift the end point up { slopeUp = (myUp - startUpY) / (mxUp - startX); endUpY = startUpY + (deltaX * slopeUp); } else // middle point above start point only: shift the start point up { slopeUp = (endUpY - myUp) / (endX - mxUp); startUpY = endUpY - (deltaX * slopeUp); } } } // brace 2 (downward) if( collideDown ) { if(( myDown >= startDownY ) && ( myDown >= endDownY )) // middle point below start and end points { // slopeDown = 0; // horizontal startDownY = myDown; endDownY = myDown; } else { if( myDown >= endDownY ) // middle point below end point only: shift the end point down { slopeDown = (myDown - startDownY) / (mxDown - startX); endDownY = startDownY + (deltaX * slopeDown); } else // middle point below start point only: shift the start point down { slopeDown = (endDownY - myDown) / (endX - mxDown); startDownY = endDownY - (deltaX * slopeDown); } } } // - Avoid being inside staff if( startUpY > 0 ) startUpY = 0; if( endUpY > 0 ) endUpY = 0; const float staffHeight = staff->getDredgeSize(); if( startDownY < staffHeight ) startDownY = staffHeight; if( endDownY < staffHeight ) endDownY = staffHeight; // - Tune float marginY = LSPACE * float(1.25); startUpY -= marginY; endUpY -= marginY; startDownY += marginY; endDownY += marginY; // - Choose the best solution (above or below) // We put the brace and the tuplet numeral on the stem side. float startY; float endY; if( stemStats >= 0 ) // stems tend to be up. { mDirection = dirUP; startY = startUpY; endY = endUpY; } else { mDirection = dirDOWN; startY = startDownY; endY = endDownY; } // - Store results const float textOffsetY = LSPACE; st->p1.x = startX; st->p1.y = startY; st->p2.x = endX; st->p2.y = endY; st->textpos.x = (startX + endX) * float(0.5); st->textpos.y = (startY + endY) * float(0.5) + textOffsetY - 9; }