void View::DrawTupletPostponed(DeviceContext *dc, Tuplet *tuplet, Layer *layer, Staff *staff) { assert(dc); assert(tuplet); assert(layer); assert(staff); if ((tuplet->GetBracketVisible() == BOOLEAN_false) && (tuplet->GetNumVisible() == BOOLEAN_false)) { tuplet->SetEmptyBB(); return; } tuplet->ResetList(tuplet); TextExtend extend; std::wstring notes; Point start, end, center; data_STEMDIRECTION direction = GetTupletCoordinates(tuplet, layer, &start, &end, ¢er); int x1 = center.x, x2 = center.x; // draw tuplet numerator if ((tuplet->GetNum() > 0) && (tuplet->GetNumVisible() != BOOLEAN_false)) { bool drawingCueSize = tuplet->GetDrawingCueSize(); dc->SetFont(m_doc->GetDrawingSmuflFont(staff->m_drawingStaffSize, drawingCueSize)); notes = IntToTupletFigures((short int)tuplet->GetNum()); if (tuplet->GetNumFormat() == tupletVis_NUMFORMAT_ratio) { notes.push_back(SMUFL_E88A_tupletColon); notes = notes + IntToTupletFigures((short int)tuplet->GetNumbase()); } dc->GetSmuflTextExtent(notes, &extend); // Calculate position for number 0x82 // since the number is slanted, move the center left int txtX = x1 - (extend.m_width / 2); // and move it further, when it is under the stave if (direction == STEMDIRECTION_down) { txtX -= staff->m_drawingStaffSize; } // we need to move down the figure of half of it height, which is about an accid width; // also, cue size is not supported. Does it has to? int txt_y = center.y - m_doc->GetGlyphWidth(SMUFL_E262_accidentalSharp, staff->m_drawingStaffSize, drawingCueSize); DrawSmuflString(dc, txtX, txt_y, notes, false, staff->m_drawingStaffSize); // x1 = 10 pixels before the number x1 = ((txtX - 40) > start.x) ? txtX - 40 : start.x; // x2 = just after, the number is abundant so I do not add anything x2 = txtX + extend.m_width + 20; dc->ResetFont(); } // Nothing to do if the bracket is not visible if (tuplet->GetBracketVisible() == BOOLEAN_false) { return; } // If all tuplets beamed draw no bracket by default if (OneBeamInTuplet(tuplet) && !(tuplet->GetBracketVisible() == BOOLEAN_true)) { return; } int verticalLine = m_doc->GetDrawingUnit(100); dc->SetPen(m_currentColour, m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize), AxSOLID); // Draw the bracket, interrupt where the number is // get the slope double m = (double)(start.y - end.y) / (double)(start.x - end.x); // calculate the y coords in the slope double y1 = (double)start.y + m * (x1 - (double)start.x); double y2 = (double)start.y + m * (x2 - (double)start.x); if (tuplet->GetNumVisible() == BOOLEAN_false) { // one single line dc->DrawLine(start.x, ToDeviceContextY(start.y), end.x, ToDeviceContextY(end.y)); } else { // first line dc->DrawLine(start.x, ToDeviceContextY(start.y), (int)x1, ToDeviceContextY((int)y1)); // second line after gap dc->DrawLine((int)x2, ToDeviceContextY((int)y2), end.x, ToDeviceContextY(end.y)); } // vertical bracket lines if (direction == STEMDIRECTION_up) { dc->DrawLine(start.x + m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(start.y), start.x + m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(start.y - verticalLine)); dc->DrawLine(end.x - m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(end.y), end.x - m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(end.y - verticalLine)); } else { dc->DrawLine(start.x + m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(start.y), start.x + m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(start.y + verticalLine)); dc->DrawLine(end.x - m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(end.y), end.x - m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize) / 2, ToDeviceContextY(end.y + verticalLine)); } dc->ResetPen(); }
data_STEMDIRECTION View::GetTupletCoordinates(Tuplet *tuplet, Layer *layer, Point *start, Point *end, Point *center) { assert(tuplet); assert(layer); assert(start); assert(end); assert(center); Point first, last; int x, y; data_STEMDIRECTION direction = STEMDIRECTION_up; ListOfObjects *tupletChildren = tuplet->GetList(tuplet); LayerElement *firstElement = dynamic_cast<LayerElement *>(tupletChildren->front()); LayerElement *lastElement = dynamic_cast<LayerElement *>(tupletChildren->back()); // AllNotesBeamed tries to figure out if all the notes are in the same beam if (OneBeamInTuplet(tuplet)) { // yes they are in a beam x = firstElement->GetDrawingX() + (lastElement->GetDrawingX() - firstElement->GetDrawingX() + lastElement->m_selfBB_x2) / 2; // align the center point at the exact center of the first an last stem // TUPLET_OFFSET is summed so it does not collide with the stem Note *firstNote = dynamic_cast<Note *>(tuplet->FindChildByType(NOTE)); Note *lastNote = dynamic_cast<Note *>(tuplet->FindChildByType(NOTE, UNLIMITED_DEPTH, BACKWARD)); y = firstElement->GetDrawingY(); if (firstNote && lastNote) { if (firstNote->GetDrawingStemDir() == STEMDIRECTION_up) y = lastNote->GetDrawingStemEnd().y + (firstNote->GetDrawingStemEnd().y - lastNote->GetDrawingStemEnd().y) / 2 + TUPLET_OFFSET; else y = lastNote->GetDrawingStemEnd().y + (firstNote->GetDrawingStemEnd().y - lastNote->GetDrawingStemEnd().y) / 2 - TUPLET_OFFSET; } // Copy the generated coordinates center->x = x; center->y = y; direction = firstNote->GetDrawingStemDir(); // stem direction is the same for all notes } else { // There are unbeamed notes of two different beams // treat all the notes as unbeamed int ups = 0, downs = 0; // quantity of up- and down-stems // In this case use the center of the notehead to calculate the exact center // as it looks better x = firstElement->GetDrawingX() + (lastElement->GetDrawingX() - firstElement->GetDrawingX() + lastElement->m_selfBB_x2) / 2; // Return the start and end position for the brackes // starting from the first edge and last of the BBoxes start->x = firstElement->m_selfBB_x1 + firstElement->GetDrawingX(); end->x = lastElement->m_selfBB_x2 + lastElement->GetDrawingX(); // The first step is to calculate all the stem directions // cycle into the elements and count the up and down dirs ListOfObjects::iterator iter = tupletChildren->begin(); while (iter != tupletChildren->end()) { if ((*iter)->Is() == NOTE) { Note *currentNote = dynamic_cast<Note *>(*iter); assert(currentNote); if (currentNote->GetDrawingStemDir() == STEMDIRECTION_up) ups++; else downs++; } ++iter; } // true means up direction = ups > downs ? STEMDIRECTION_up : STEMDIRECTION_down; // if ups or downs are 0, it means all the stems go in the same direction if (ups == 0 || downs == 0) { Note *firstNote = dynamic_cast<Note *>(tuplet->FindChildByType(NOTE)); Note *lastNote = dynamic_cast<Note *>(tuplet->FindChildByType(NOTE, UNLIMITED_DEPTH, BACKWARD)); // Calculate the average between the first and last stem // set center, start and end too. y = firstElement->GetDrawingY(); if (firstNote && lastNote) { if (direction == STEMDIRECTION_up) { // up y = lastNote->GetDrawingStemEnd().y + (firstNote->GetDrawingStemEnd().y - lastNote->GetDrawingStemEnd().y) / 2 + TUPLET_OFFSET; start->y = firstNote->GetDrawingStemEnd().y + TUPLET_OFFSET; end->y = lastNote->GetDrawingStemEnd().y + TUPLET_OFFSET; } else { y = lastNote->GetDrawingStemEnd().y + (firstNote->GetDrawingStemEnd().y - lastNote->GetDrawingStemEnd().y) / 2 - TUPLET_OFFSET; start->y = firstNote->GetDrawingStemEnd().y - TUPLET_OFFSET; end->y = lastNote->GetDrawingStemEnd().y - TUPLET_OFFSET; } } // Now we cycle again in all the intermediate notes (i.e. we start from the second note // and stop at last -1) // We will see if the position of the note is more (or less for down stems) of the calculated // average. In this case we offset down or up all the points iter = tupletChildren->begin(); while (iter != tupletChildren->end()) { if ((*iter)->Is() == NOTE) { Note *currentNote = dynamic_cast<Note *>(*iter); assert(currentNote); if (direction == STEMDIRECTION_up) { // The note is more than the avg, adjust to y the difference // from this note to the avg if (currentNote->GetDrawingStemEnd().y + TUPLET_OFFSET > y) { int offset = y - (currentNote->GetDrawingStemEnd().y + TUPLET_OFFSET); y -= offset; end->y -= offset; start->y -= offset; } } else { if (currentNote->GetDrawingStemEnd().y - TUPLET_OFFSET < y) { int offset = y - (currentNote->GetDrawingStemEnd().y - TUPLET_OFFSET); y -= offset; end->y -= offset; start->y -= offset; } } } ++iter; } } else { // two-directional beams // this case is similar to the above, but the bracket is only horizontal // y is 0 because the final y pos is above the tallest stem y = 0; // Find the tallest stem and set y to it (with the offset distance) iter = tupletChildren->begin(); while (iter != tupletChildren->end()) { if ((*iter)->Is() == NOTE) { Note *currentNote = dynamic_cast<Note *>(*iter); assert(currentNote); if (currentNote->GetDrawingStemDir() == direction) { if (direction == STEMDIRECTION_up) { if (y == 0 || currentNote->GetDrawingStemEnd().y + TUPLET_OFFSET >= y) y = currentNote->GetDrawingStemEnd().y + TUPLET_OFFSET; } else { if (y == 0 || currentNote->GetDrawingStemEnd().y - TUPLET_OFFSET <= y) y = currentNote->GetDrawingStemEnd().y - TUPLET_OFFSET; } } else { // do none for now // but if a notehead with a reversed stem is taller that the last // calculated y, we need to offset } } ++iter; } // end and start are on the same line (and so il center when set later) end->y = start->y = y; } } center->x = x; center->y = y; return direction; }
bool View::GetTupletCoordinates(Tuplet* tuplet, Layer *layer, MusPoint* start, MusPoint* end, MusPoint *center) { MusPoint first, last; int x, y; bool direction = true; //true = up, false = down LayerElement *firstNote, *lastNote; // AllNotesBeamed tries to figure out if all the notes are in the same beam if (OneBeamInTuplet(tuplet)) { firstNote = dynamic_cast<LayerElement*>(tuplet->m_list.front()); lastNote = dynamic_cast<LayerElement*>(tuplet->m_list.back()); // yes they are in a beam // get the x position centered from the STEM so it looks better // NOTE start and end are left to 0, this is the signal that no bracket has to be drawn x = firstNote->m_drawingStemStart.x + (lastNote->m_drawingStemStart.x - firstNote->m_drawingStemStart.x) / 2; // align the center point at the exact center of the first an last stem // TUPLET_OFFSET is summed so it does not collide with the stem if (firstNote->m_drawingStemDir) y = lastNote->m_drawingStemEnd.y + (firstNote->m_drawingStemEnd.y - lastNote->m_drawingStemEnd.y) / 2 + TUPLET_OFFSET; else y = lastNote->m_drawingStemEnd.y + (firstNote->m_drawingStemEnd.y - lastNote->m_drawingStemEnd.y) / 2 - TUPLET_OFFSET; // Copy the generated coordinates center->x = x; center->y = y; direction = firstNote->m_drawingStemDir; // stem direction is same for all notes } else { //ArrayOfObjects all_notes; // We can have a mixed group of Beams and notes. Flatten it /* for (unsigned int i = 0; i < tuplet->m_children.size(); i++) { if (dynamic_cast<Note*>(tuplet->m_children[i])) all_notes.push_back(tuplet->m_children[i]); if (dynamic_cast<Beam*>(tuplet->m_children[i])) { Beam* beam = dynamic_cast<Beam*>(tuplet->m_children[i]); std::copy( beam->m_list.begin(), beam->m_list.end(), std::back_inserter( all_notes ) ); } }*/ firstNote = dynamic_cast<LayerElement*>(tuplet->m_list.front()); lastNote = dynamic_cast<LayerElement*>(tuplet->m_list.back()); // There are unbeamed notes of two different beams // treat all the notes as unbeames int ups = 0, downs = 0; // quantity of up- and down-stems // In this case use the center of the notehead to calculate the exact center // as it looks better x = firstNote->GetDrawingX() + (lastNote->GetDrawingX() - firstNote->GetDrawingX() + lastNote->m_selfBB_x2) / 2; // Return the start and end position for the brackes // starting from the first edge and last of the BBoxes start->x = firstNote->m_selfBB_x1 + firstNote->GetDrawingX(); end->x = lastNote->m_selfBB_x2 + lastNote->GetDrawingX(); // THe first step is to calculate all the stem directions // cycle into the elements and count the up and down dirs ListOfObjects::iterator iter = tuplet->m_list.begin(); while (iter != tuplet->m_list.end()) { LayerElement *currentNote = dynamic_cast<LayerElement*>(*iter); if (currentNote->m_drawingStemDir == true) ups++; else downs++; ++iter; } // true means up direction = ups > downs ? true : false; // if ups or downs is 0, it means all the stems go in the same direction if (ups == 0 || downs == 0) { // Calculate the average between the first and last stem // set center, start and end too. if (direction) { // up y = lastNote->m_drawingStemEnd.y + (firstNote->m_drawingStemEnd.y - lastNote->m_drawingStemEnd.y) / 2 + TUPLET_OFFSET; start->y = firstNote->m_drawingStemEnd.y + TUPLET_OFFSET; end->y = lastNote->m_drawingStemEnd.y + TUPLET_OFFSET; } else { y = lastNote->m_drawingStemEnd.y + (firstNote->m_drawingStemEnd.y - lastNote->m_drawingStemEnd.y) / 2 - TUPLET_OFFSET; start->y = firstNote->m_drawingStemEnd.y - TUPLET_OFFSET; end->y = lastNote->m_drawingStemEnd.y - TUPLET_OFFSET; } // Now we cycle again in all the intermediate notes (i.e. we start from the second note // and stop at last -1) // We will see if the position of the note is more (or less for down stems) of the calculated // average. In this case we offset down or up all the points iter = tuplet->m_list.begin(); while (iter != tuplet->m_list.end()) { LayerElement *currentNote = dynamic_cast<LayerElement*>(*iter); if (direction) { // The note is more than the avg, adjust to y the difference // from this note to the avg if (currentNote->m_drawingStemEnd.y + TUPLET_OFFSET > y) { int offset = y - (currentNote->m_drawingStemEnd.y + TUPLET_OFFSET); y -= offset; end->y -= offset; start->y -= offset; } } else { if (currentNote->m_drawingStemEnd.y - TUPLET_OFFSET < y) { int offset = y - (currentNote->m_drawingStemEnd.y - TUPLET_OFFSET); y -= offset; end->y -= offset; start->y -= offset; } } ++iter; } } else { // two directional beams // this case is similar to the above, but the bracket is only orizontal // y is 0 because the final y pos is above the tallest stem y = 0; // Find the tallest stem and set y to it (with the offset distance) iter = tuplet->m_list.begin(); while (iter != tuplet->m_list.end()) { LayerElement *currentNote = dynamic_cast<LayerElement*>(*iter); if (currentNote->m_drawingStemDir == direction) { if (direction) { if (y == 0 || currentNote->m_drawingStemEnd.y + TUPLET_OFFSET >= y) y = currentNote->m_drawingStemEnd.y + TUPLET_OFFSET; } else { if (y == 0 || currentNote->m_drawingStemEnd.y - TUPLET_OFFSET <= y) y = currentNote->m_drawingStemEnd.y - TUPLET_OFFSET; } } else { // do none for now // but if a notehead with a reversed stem is taller that the last // calculated y, we need to offset } ++iter; } // end and start are on the same line (and so il center when set later) end->y = start->y = y; } } center->x = x; center->y = y; return direction; }