Beispiel #1
0
/** \brief Returns a page corresponding to a time position.

	It finds the musical element which time position is immediately before or equal to
	the input time position. Then it returns the page of that musical element.

	getPageForTimePos() always try to return a page, while getPageNum() only returns
	a page when the input time position match the time position of an existing
	musical element.

	TODO: Check if the page keeps track of both its start timepos and end timepos,
			therefore we could directly jump from one page to another


	\bug The last system of a page point to the next page instead of its parent page.

*/
GRPage * GRVoice::getPageForTimePos( int num, int denom ) const
{
    GRPage * outPage = 0;
    TYPE_TIMEPOSITION inTime ( num, denom );
    GuidoPos pos = First();

    // - Find the event which best match the input date
    GREvent * bestEv = 0;
    while (pos)
    {
        GRNotationElement * el = GetNext(pos);
        GREvent * ev = GREvent::cast(el);
        if( ev )
        {
            const TYPE_TIMEPOSITION & currTime = ev->getRelativeTimePosition();
            if( currTime <= inTime )
                bestEv = ev;
            else
                break;	// (JB) I suppose that all next elements have a greater time pos.
        }
    }

    // - Get the page corresponding to the event found

    if( bestEv )
    {
        GRStaff * staff = bestEv->getGRStaff();
        GRSystem * sys = staff->getGRSystem();
        outPage = sys->getGRPage();
    }

    return outPage;
}
Beispiel #2
0
GRPage * GRVoice::getPageNum(int num,int denom)
{
    // I have to travers the voice and find the element
    // at the position num/denom. Then I have to find the
    // pagenumber of that element.

    const TYPE_TIMEPOSITION tpsearch ( num,denom );
    GuidoPos pos = First();
    while (pos)
    {
        GRNotationElement * el = GetNext(pos);
        GREvent * ev = GREvent::cast(el);
        if (ev)
        {
            if (ev->getRelativeTimePosition() == tpsearch)
            {
                GRStaff * stf = ev->getGRStaff();
                GRSystem * sys = stf->getGRSystem();
                GRPage * page = sys->getGRPage();
                return page;
            }
            else if (ev->getRelativeTimePosition() > tpsearch)
                return NULL;
        }
    }
    return NULL;
}
Beispiel #3
0
// (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;
	}
}
Beispiel #4
0
// (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();

	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!
	if ( (startEl && startEl->getStemLengthSet() && endEl && endEl->getStemLengthSet())
		|| tagtype == SYSTEMTAG || (arBeam && isSpecBeam))
	{
		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;
				
				float offbase = 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 );				
			}
			if (oldpos == sse->endpos)
				break;
		}
	}	
	
	// -- 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 defines 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;
	// for beam length adjustment - DF sept 15 2009
	const float xadjust = infos.currentLSPACE/10;
	GDirection lastLocalDir = dirOFF;
	int previousBeamsCount = 0;
	while (pos)
	{
		GuidoPos oldpos = pos;
		GREvent * stemNote = GREvent::cast(mAssociated->GetNext(pos));
		if (stemNote)
		{
			GDirection localDir = stemNote->getStemDirection();
			float yLocalFact1 = yFact1 * localDir;
			float yLocalFact2 = yFact2 * localDir;

			// 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;
	}
	
	// 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;
	}
}
Beispiel #5
0
// ----------------------------------------------------------------------------
void 
GRTuplet::manualPosition(GObject * caller, const NVPoint & inPos )
{
	GREvent * event = GREvent::cast( caller );
	if( event == 0 ) return;

	GRStaff * staff = event->getGRStaff();
	if( staff == 0 ) return;

	GRSystemStartEndStruct * sse = getSystemStartEndStruct(staff->getGRSystem());
	if( sse == 0 ) return;

	GRNotationElement * startElement = sse->startElement;
	GRNotationElement * endElement = sse->endElement;

	// if ( openLeftRange && openRightRange ) return;

	GRTupletSaveStruct * st = (GRTupletSaveStruct *)sse->p;

	const ARTuplet * arTuplet = getARTuplet();

	float dy1 = arTuplet->isDySet() ? arTuplet->getDy1() : 0;
	float dy2 = arTuplet->isDySet() ? arTuplet->getDy2() : 0;

	if(( dy1 > 0 ) || ( dy2 > 0 ))
		mDirection = dirUP;

	const float halfNoteWidth = LSPACE * float(0.65); // harcoded
	if (event == startElement)
	{
		st->p1 = startElement->getPosition();		
		st->p1.x -= halfNoteWidth; // to enclose all the element width
		st->p1.y -= dy1;
	}
	else if (event == endElement)
	{
		st->p2 = endElement->getPosition();
		st->p2.x += halfNoteWidth; // to enclose all the element width
		st->p2.y -= dy2;
	}
	
	if(event == endElement || (endElement == 0 && event == startElement))
	{
		if (startElement && endElement)
		{
			const float posx = (st->p2.x - st->p1.x) * 0.5f + st->p1.x;
            const float posy = st->p2.y > st->p1.y ? (st->p2.y - st->p1.y) * 0.5f + st->p1.y + 40 : (st->p1.y - st->p2.y) * 0.5f + st->p2.y + 40;
		
			st->textpos.x = posx;
            st->textpos.y = posy;
		}
		else
			st->textpos = inPos;
	}

//	if( arTuplet->isFormatSet())
//	{
		mShowLeftBrace = arTuplet->getLeftBrace();
		mShowRightBrace = arTuplet->getRightBrace();
//	}

}
Beispiel #6
0
/** 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;
}