/** Draw this object */ public override void draw(IGraphics graphics) { int multiNodeCount = getMultiNodeCount(); if (multiNodeCount == 0) { // Only the defining Beam draws the beams for all in the multi-node group return; } if (multiNodeCount == 1) { // DEBUG: should handle this case return; } FinalPoint left = getScreenHotspot(); double slope = getSlope(); int maxParts = getMaxParts(); int stemDirection = getStemDirection(); for (int i = 0; i < multiNodeCount; ++i) { Beam beam = getMultiNode(i); Beam previousBeam = null, nextBeam = null; if (i > 0) { previousBeam = getMultiNode(i - 1); } if (i < (multiNodeCount - 1)) { nextBeam = getMultiNode(i + 1); } FinalPoint stemTip = beam.getAnchor().getScreenHotspot(); // Get X and Y for this beam int beamX = stemTip.x; int beamY = (int)(left.y + slope * (beamX - left.x)); // First, extend the stem. graphics.DrawLine(beamX, beamY, stemTip.x, stemTip.y); // For each of the possible parts, draw as needed to the left or right for (int part = 1; part <= maxParts; part++) { if (i > 0) { // Only do parts to left if we are not the leftmost stem if (part <= beam._partsToLeft && part > previousBeam._partsToRight) { // Draw a stub to the left // DEBUG: should make sure this doesn't run into the other stem drawBeam(graphics, beamX, beamY, beamX - 6); } // The case where the beam would extend all the way was handled // by the previousBeam drawing to here. } if (i < (multiNodeCount - 1)) { // Only do parts to right if we are not the rightmost stem if (part <= beam._partsToRight && part > nextBeam._partsToLeft) { // Draw a stub to the right // DEBUG: should make sure this doesn't run into the other stem drawBeam(graphics, beamX, beamY, beamX + 6); } if (part <= beam._partsToRight && part <= nextBeam._partsToLeft) { // Draw beam extended over to the other drawBeam(graphics, beamX, beamY, nextBeam.getAnchor().getScreenHotspot().x); } } beamY += -stemDirection * SPACING; } } }
/** If this is the defining node, return the screen hotspot, computing it if * called for the first time. The screen hotspot for the beam group is * the position of the left end of the beam group at the beam farthest from * the notehead. If not the defining node, return the * anchor hotspot. * Strictly speaking, this is the hotspot of the left end of the beam, which * is not necessarily the defining node. * This also sets _maxParts and other values used internally. * * @return The position of the hotspot in screen coordinates. */ public override FinalPoint getScreenHotspot() { if (_screenHotspot != null) { // We have already computed it return(_screenHotspot); } int multiNodeCount = getMultiNodeCount(); if (multiNodeCount == 0) { // Not the defining node. Just use the anchor's hot spot _screenHotspot = getAnchor().getScreenHotspot(); return(_screenHotspot); } if (multiNodeCount == 1) { // This is an unexpected case with the beam on only one stem. _screenHotspot = getAnchor().getScreenHotspot(); return(_screenHotspot); } // A beam is either horizontal (if the first and last stem tip are // at the same y value) or slanted with one end of the beam // 5 points higher or lower than the beginning, based on whether // the last stem tip is higher or lower. // Don't assume that this defining node is the leftmost, but do // assume multiNode(0) is leftmost. // Assume the anchor for a beam is the stem whos hotspot is its tip. Point hotspot = getMultiNode(0).getAnchor().getScreenHotspot().newPoint(); FinalPoint rightmost = getMultiNode(multiNodeCount - 1).getAnchor().getScreenHotspot(); if (rightmost.y == hotspot.y) { _slope = 0.0; } else if (rightmost.y > hotspot.y) { _slope = 5.0 / (rightmost.x - hotspot.x); } else { _slope = -5.0 / (rightmost.x - hotspot.x); } // DEBUG: this assumes all stems in the same direction. _stemDirection = ((Stem)getMultiNode(0).getAnchor()).getStemDown() ? 1 : -1; // The Y position along the beam at x is // hotspot.y + _slope * (x - hotspot.x). // Check each stem tip and adjust the hotspot.y as necessary // to make sure the beam does not cross the stem. for (int i = 1; i < multiNodeCount; ++i) { FinalPoint stemTip = getMultiNode(i).getAnchor().getScreenHotspot(); int beamY = (int)(hotspot.y + _slope * (stemTip.x - hotspot.x)); // Note that larger Y is lower on the screen! // _stemDirection > 0 means stems down. if (_stemDirection > 0 && stemTip.y > beamY) { hotspot.translate(0, stemTip.y - beamY); } else if (_stemDirection < 0 && stemTip.y < beamY) { hotspot.translate(0, -(beamY - stemTip.y)); } } // Compute _maxParts to be used later. _maxParts = 0; for (int i = 0; i < multiNodeCount; ++i) { Beam beam = getMultiNode(i); if (beam._partsToLeft > _maxParts) { _maxParts = beam._partsToLeft; } if (beam._partsToRight > _maxParts) { _maxParts = beam._partsToRight; } } // Move the hotspot to the beam farthest from the notehead. hotspot.translate(0, (_maxParts - 1) * _stemDirection * SPACING); if (_maxParts > 1) { // As a special case for 16th notes and higher, move the beams // closer to the notehead. hotspot.translate(0, -_stemDirection * SPACING); } _screenHotspot = new FinalPoint(hotspot); return(_screenHotspot); }