@@ -29,7 +29,7 @@ import { ScoreBeatContainerGlyph } from '@src/rendering/ScoreBeatContainerGlyph'
2929import { ScoreRenderer } from '@src/rendering/ScoreRenderer' ;
3030import { AccidentalHelper } from '@src/rendering/utils/AccidentalHelper' ;
3131import { BeamDirection } from '@src/rendering/utils/BeamDirection' ;
32- import { BeamingHelper } from '@src/rendering/utils/BeamingHelper' ;
32+ import { BeamingHelper , BeamingHelperDrawInfo } from '@src/rendering/utils/BeamingHelper' ;
3333import { RenderingResources } from '@src/RenderingResources' ;
3434import { Settings } from '@src/Settings' ;
3535import { ModelUtils } from '@src/model/ModelUtils' ;
@@ -354,71 +354,104 @@ export class ScoreBarRenderer extends BarRendererBase {
354354 private calculateBeamYWithDirection ( h : BeamingHelper , x : number , direction : BeamDirection ) : number {
355355 let stemSize : number = this . getStemSize ( h ) ;
356356
357- const firstBeat = h . beats [ 0 ] ;
357+ if ( ! h . drawingInfos . has ( direction ) ) {
358+ let drawingInfo = new BeamingHelperDrawInfo ( ) ;
359+ h . drawingInfos . set ( direction , drawingInfo ) ;
358360
359- // create a line between the min and max note of the group
360- if ( h . beats . length === 1 ) {
361- if ( direction === BeamDirection . Up ) {
362- return this . getScoreY ( this . accidentalHelper . getMinLine ( firstBeat ) ) - stemSize ;
363- }
364- return this . getScoreY ( this . accidentalHelper . getMaxLine ( firstBeat ) ) + stemSize ;
365- }
361+ // the beaming logic works like this:
362+ // 1. we take the first and last note, add the stem, and put a diagnal line between them.
363+ // 2. the height of the diagonal line must not exceed a max height,
364+ // - if this is the case, the line on the more distant note just gets longer
365+ // 3. any middle elements (notes or rests) shift this diagonal line up/down to avoid overlaps
366366
367- const lastBeat = h . beats [ h . beats . length - 1 ] ;
367+ const firstBeat = h . beats [ 0 ] ;
368+ const lastBeat = h . beats [ h . beats . length - 1 ] ;
368369
369- // we use the min/max notes to place the beam along their real position
370- // we only want a maximum of 10 offset for their gradient
371- let maxDistance : number = 10 * this . scale ;
372- // if the min note is not first or last, we can align notes directly to the position
373- // of the min note
374- const beatOfLowestNote = h . beatOfLowestNote ;
375- const beatOfHighestNote = h . beatOfHighestNote ;
376- if (
377- direction === BeamDirection . Down &&
378- beatOfLowestNote !== firstBeat &&
379- beatOfLowestNote !== lastBeat
380- ) {
381- return this . getScoreY ( this . accidentalHelper . getMaxLine ( beatOfLowestNote ) ) + stemSize ;
382- }
383- if (
384- direction === BeamDirection . Up &&
385- beatOfHighestNote !== firstBeat &&
386- beatOfHighestNote !== lastBeat
387- ) {
388- return this . getScoreY ( this . accidentalHelper . getMinLine ( beatOfHighestNote ) ) - stemSize ;
389- }
370+ // 1. put direct diagonal line.
371+ drawingInfo . startX = h . getBeatLineX ( firstBeat ) ;
372+ drawingInfo . startY =
373+ direction === BeamDirection . Up
374+ ? this . getScoreY ( this . accidentalHelper . getMinLine ( firstBeat ) ) - stemSize
375+ : this . getScoreY ( this . accidentalHelper . getMaxLine ( firstBeat ) ) + stemSize ;
390376
391- let startX : number = h . getBeatLineX ( firstBeat ) ;
392- let startY : number =
393- direction === BeamDirection . Up
394- ? this . getScoreY ( this . accidentalHelper . getMinLine ( firstBeat ) ) - stemSize
395- : this . getScoreY ( this . accidentalHelper . getMaxLine ( firstBeat ) ) + stemSize ;
377+ drawingInfo . endX = h . getBeatLineX ( lastBeat ) ;
378+ drawingInfo . endY =
379+ direction === BeamDirection . Up
380+ ? this . getScoreY ( this . accidentalHelper . getMinLine ( lastBeat ) ) - stemSize
381+ : this . getScoreY ( this . accidentalHelper . getMaxLine ( lastBeat ) ) + stemSize ;
382+
383+ // 2. ensure max height
384+ // we use the min/max notes to place the beam along their real position
385+ // we only want a maximum of 10 offset for their gradient
386+ let maxDistance : number = 10 * this . scale ;
387+ if ( direction === BeamDirection . Down && drawingInfo . startY > drawingInfo . endY && drawingInfo . startY - drawingInfo . endY > maxDistance ) {
388+ drawingInfo . endY = drawingInfo . startY - maxDistance ;
389+ }
390+ if ( direction === BeamDirection . Down && drawingInfo . endY > drawingInfo . startY && drawingInfo . endY - drawingInfo . startY > maxDistance ) {
391+ drawingInfo . startY = drawingInfo . endY - maxDistance ;
392+ }
393+ if ( direction === BeamDirection . Up && drawingInfo . startY < drawingInfo . endY && drawingInfo . endY - drawingInfo . startY > maxDistance ) {
394+ drawingInfo . endY = drawingInfo . startY + maxDistance ;
395+ }
396+ if ( direction === BeamDirection . Up && drawingInfo . endY < drawingInfo . startY && drawingInfo . startY - drawingInfo . endY > maxDistance ) {
397+ drawingInfo . startY = drawingInfo . endY + maxDistance ;
398+ }
396399
397- let endX : number = h . getBeatLineX ( lastBeat ) ;
398- let endY : number =
399- direction === BeamDirection . Up
400- ? this . getScoreY ( this . accidentalHelper . getMinLine ( lastBeat ) ) - stemSize
401- : this . getScoreY ( this . accidentalHelper . getMaxLine ( lastBeat ) ) + stemSize ;
400+ // 3. let middle elements shift up/down
401+ if ( h . beats . length > 1 ) {
402+ // check if highest note shifts bar up or down
403+ if ( direction === BeamDirection . Up ) {
404+ let yNeededForHighestNote = this . getScoreY ( this . accidentalHelper . getMinLine ( h . beatOfHighestNote ) ) - stemSize ;
405+ const yGivenByCurrentValues = drawingInfo . calcY ( h . getBeatLineX ( h . beatOfHighestNote ) ) ;
406+
407+ const diff = yGivenByCurrentValues - yNeededForHighestNote ;
408+ if ( diff > 0 ) {
409+ drawingInfo . startY -= diff ;
410+ drawingInfo . endY -= diff ;
411+ }
412+ } else {
413+ let yNeededForLowestNote = this . getScoreY ( this . accidentalHelper . getMaxLine ( h . beatOfLowestNote ) ) + stemSize ;
414+ const yGivenByCurrentValues = drawingInfo . calcY ( h . getBeatLineX ( h . beatOfLowestNote ) ) ;
415+
416+ const diff = yNeededForLowestNote - yGivenByCurrentValues ;
417+ if ( diff > 0 ) {
418+ drawingInfo . startY += diff ;
419+ drawingInfo . endY += diff ;
420+ }
421+ }
402422
403- // ensure the maxDistance
404- if ( direction === BeamDirection . Down && startY > endY && startY - endY > maxDistance ) {
405- endY = startY - maxDistance ;
406- }
407- if ( direction === BeamDirection . Down && endY > startY && endY - startY > maxDistance ) {
408- startY = endY - maxDistance ;
409- }
410- if ( direction === BeamDirection . Up && startY < endY && endY - startY > maxDistance ) {
411- endY = startY + maxDistance ;
412- }
413- if ( direction === BeamDirection . Up && endY < startY && startY - endY > maxDistance ) {
414- startY = endY + maxDistance ;
415- }
416- // get the y position of the given beat on this curve
417- if ( startX === endX ) {
418- return startY ;
423+ // check if rest shifts bar up or down
424+ if ( h . minRestLine !== null || h . maxRestLine !== null ) {
425+ const barCount : number = ModelUtils . getIndex ( h . shortestDuration ) - 2 ;
426+ let scaleMod : number = h . isGrace ? NoteHeadGlyph . GraceScale : 1 ;
427+ let barSpacing : number = barCount *
428+ ( BarRendererBase . BeamSpacing + BarRendererBase . BeamThickness ) * this . scale * scaleMod ;
429+ barSpacing += BarRendererBase . BeamSpacing ;
430+
431+ if ( direction === BeamDirection . Up && h . minRestLine !== null ) {
432+ let yNeededForRest = this . getScoreY ( h . minRestLine ! ) - barSpacing ;
433+ const yGivenByCurrentValues = drawingInfo . calcY ( h . getBeatLineX ( h . beatOfMinRestLine ! ) ) ;
434+
435+ const diff = yGivenByCurrentValues - yNeededForRest ;
436+ if ( diff > 0 ) {
437+ drawingInfo . startY -= diff ;
438+ drawingInfo . endY -= diff ;
439+ }
440+ } else if ( direction === BeamDirection . Down && h . maxRestLine !== null ) {
441+ let yNeededForRest = this . getScoreY ( h . maxRestLine ! ) + barSpacing ;
442+ const yGivenByCurrentValues = drawingInfo . calcY ( h . getBeatLineX ( h . beatOfMaxRestLine ! ) ) ;
443+
444+ const diff = yNeededForRest - yGivenByCurrentValues ;
445+ if ( diff > 0 ) {
446+ drawingInfo . startY += diff ;
447+ drawingInfo . endY += diff ;
448+ }
449+ }
450+ }
451+ }
419452 }
420- // y(x) = ( (y2 - y1) / (x2 - x1) ) * (x - x1) + y1;
421- return ( ( endY - startY ) / ( endX - startX ) ) * ( x - startX ) + startY ;
453+
454+ return h . drawingInfos . get ( direction ) ! . calcY ( x ) ;
422455 }
423456
424457
0 commit comments