calculateSlope() {
    const {
      notes,
      stem_direction: stemDirection,
      render_options: { max_slope, min_slope, slope_iterations, slope_cost },
    } = this;

    const firstNote = notes[0];
    const initialSlope = getStemSlope(firstNote, notes[notes.length - 1]);
    const increment = (max_slope - min_slope) / slope_iterations;
    let minCost = Number.MAX_VALUE;
    let bestSlope = 0;
    let yShift = 0;

    // iterate through slope values to find best weighted fit
    for (let slope = min_slope; slope <= max_slope; slope += increment) {
      let totalStemExtension = 0;
      let yShiftTemp = 0;

      // iterate through notes, calculating y shift and stem extension
      for (let i = 1; i < notes.length; ++i) {
        const note = notes[i];
        const adjustedStemTipY = this.getSlopeY(
          note.getStemX(),
          firstNote.getStemX(),
          firstNote.getStemExtents().topY,
          slope
        ) + yShiftTemp;

        const stemTipY = note.getStemExtents().topY;
        // beam needs to be shifted up to accommodate note
        if (stemTipY * stemDirection < adjustedStemTipY * stemDirection) {
          const diff = Math.abs(stemTipY - adjustedStemTipY);
          yShiftTemp += diff * -stemDirection;
          totalStemExtension += diff * i;
        } else { // beam overshoots note, account for the difference
          totalStemExtension += (stemTipY - adjustedStemTipY) * stemDirection;
        }
      }

      // most engraving books suggest aiming for a slope about half the angle of the
      // difference between the first and last notes' stem length;
      const idealSlope = initialSlope / 2;
      const distanceFromIdeal = Math.abs(idealSlope - slope);

      // This tries to align most beams to something closer to the idealSlope, but
      // doesn't go crazy. To disable, set this.render_options.slope_cost = 0
      const cost = slope_cost * distanceFromIdeal + Math.abs(totalStemExtension);

      // update state when a more ideal slope is found
      if (cost < minCost) {
        minCost = cost;
        bestSlope = slope;
        yShift = yShiftTemp;
      }
    }

    this.slope = bestSlope;
    this.y_shift = yShift;
  }
// Calculate the best possible slope for the provided notes
Calculate the best possible slope for the provided notes
Calculate the best possible slope for the provided notes
calculateSlope() {
    const {
      notes,
      stem_direction: stemDirection,
      render_options: { max_slope, min_slope, slope_iterations, slope_cost },
    } = this;
    const firstNote = notes[0];
    const initialSlope = getStemSlope(firstNote, notes[notes.length - 1]);
    const increment = (max_slope - min_slope) / slope_iterations;
    let minCost = Number.MAX_VALUE;
    let bestSlope = 0;
    let yShift = 0;
    for (let slope = min_slope; slope <= max_slope; slope += increment) {
      let totalStemExtension = 0;
      let yShiftTemp = 0;
      for (let i = 1; i < notes.length; ++i) {
        const note = notes[i];
        const adjustedStemTipY = this.getSlopeY(
          note.getStemX(),
          firstNote.getStemX(),
          firstNote.getStemExtents().topY,
          slope
        ) + yShiftTemp;
        const stemTipY = note.getStemExtents().topY;
        if (stemTipY * stemDirection < adjustedStemTipY * stemDirection) {
          const diff = Math.abs(stemTipY - adjustedStemTipY);
          yShiftTemp += diff * -stemDirection;
          totalStemExtension += diff * i;
        } else { 
          totalStemExtension += (stemTipY - adjustedStemTipY) * stemDirection;
        }
      }
      const idealSlope = initialSlope / 2;
      const distanceFromIdeal = Math.abs(idealSlope - slope);
      const cost = slope_cost * distanceFromIdeal + Math.abs(totalStemExtension);
      if (cost < minCost) {
        minCost = cost;
        bestSlope = slope;
        yShift = yShiftTemp;
      }
    }
    this.slope = bestSlope;
    this.y_shift = yShift;
  }
calculateSlope ( ) { const { notes , stem_direction : stemDirection , render_options : { max_slope , min_slope , slope_iterations , slope_cost } , } = this ; const firstNote = notes [ 0 ] ; const initialSlope = getStemSlope ( firstNote , notes [ notes . length - 1 ] ) ; const increment = ( max_slope - min_slope ) / slope_iterations ; let minCost = Number . MAX_VALUE ; let bestSlope = 0 ; let yShift = 0 ; for ( let slope = min_slope ; slope <= max_slope ; slope += increment ) { let totalStemExtension = 0 ; let yShiftTemp = 0 ; for ( let i = 1 ; i < notes . length ; ++ i ) { const note = notes [ i ] ; const adjustedStemTipY = this . getSlopeY ( note . getStemX ( ) , firstNote . getStemX ( ) , firstNote . getStemExtents ( ) . topY , slope ) + yShiftTemp ; const stemTipY = note . getStemExtents ( ) . topY ; if ( stemTipY * stemDirection < adjustedStemTipY * stemDirection ) { const diff = Math . abs ( stemTipY - adjustedStemTipY ) ; yShiftTemp += diff * - stemDirection ; totalStemExtension += diff * i ; } else { totalStemExtension += ( stemTipY - adjustedStemTipY ) * stemDirection ; } } const idealSlope = initialSlope / 2 ; const distanceFromIdeal = Math . abs ( idealSlope - slope ) ; const cost = slope_cost * distanceFromIdeal + Math . abs ( totalStemExtension ) ; if ( cost < minCost ) { minCost = cost ; bestSlope = slope ; yShift = yShiftTemp ; } } this . slope = bestSlope ; this . y_shift = yShift ; }
Calculate the best possible slope for the provided notes
Calculate the best possible slope for the provided notes
// iterate through slope values to find best weighted fit // iterate through notes, calculating y shift and stem extension // beam needs to be shifted up to accommodate note // beam overshoots note, account for the difference // most engraving books suggest aiming for a slope about half the angle of the // difference between the first and last notes' stem length; // This tries to align most beams to something closer to the idealSlope, but // doesn't go crazy. To disable, set this.render_options.slope_cost = 0 // update state when a more ideal slope is found