<> <> <> <> <> <> DIRECTORY Beam USING [Draw, Grace, InVoice], Chord USING [Draw, Width], Convert USING [RopeFromInt], Imager USING [LineTo, MaskFill, MoveTo, SetXY, Trajectory], MusicDefs, Note USING [Draw, Width], Real USING [FixI], Sheet USING [FindLine, Height, MapHeight, NextLine], Utility USING [DrawLine, DrawString, SetColor, SetFont]; BeamImplB: CEDAR PROGRAM IMPORTS Beam, Chord, Convert, Imager, Note, Real, Sheet, Utility EXPORTS Beam = BEGIN OPEN MusicDefs, Utility; <<****************************************************************************>> <> <<****************************************************************************>> Draw: PUBLIC PROC[score: ScorePTR, b: BeamPTR] RETURNS[INTEGER, INTEGER] = { start: Time; a: BeamArray; slope: REAL _ 5; i, end: CARDINAL; inVoice: BOOL; sheet: SheetPTR _ score.sheet; nonGrace: BOOL _ FALSE; thickness, lineHeight: INTEGER; level, space, s, k, up, d: CARDINAL _ 0; x0, y0, xn, yn, xi, yi, overX: REAL _ 1; j, value, beamValue: NoteValue _ unknown; dotted, oldDotted, stemUp, none: BOOL _ FALSE; IF NOT sheet.notehead THEN {DrawPhysicalBeam[score, b]; RETURN[0, 0]}; IF NOT b.beamed THEN { DrawNTuple[score, b]; RETURN[0, 0]; }; start _ b.sync1.time; [x0, y0] _ Sheet.MapHeight[sheet, b.sync1.time, b.height+Sheet.Height[sheet, b.sync1.time, , b.staff]]; overX _ sheet.section[Sheet.FindLine[sheet, b.sync2.time]].x; inVoice _ Beam.InVoice[b, score.sheet.voice]; IF b.tilt > -x0/slope AND b.tilt < x0/slope THEN space _ 5 ELSE space _ 7; IF Beam.Grace[b] THEN {space _ space-2; thickness _ 0} ELSE thickness _ 1; -- thickness 1, 2 <> FOR i: CARDINAL IN [0..b.length) DO WITH b.chord[i] SELECT FROM n: NotePTR => { IF sheet.display # graphical THEN n.delta _ 0; IF i = 0 THEN start _ start + n.delta + (IF n.stemUp THEN Note.Width[n] ELSE 0); IF i = 0 THEN x0 _ x0 + n.delta + (IF n.stemUp THEN Note.Width[n] ELSE 0); a[i].x _ n.sync.time - start + n.delta; IF n.stemUp THEN a[i].x _ a[i].x + Note.Width[n]; a[i].y _ y0 + b.tilt*a[i].x; a[i].value _ n.value; a[i].stemUp _ n.stemUp; a[i].dotted _ n.dotted; }; c: ChordPTR => { IF sheet.display # graphical THEN c.delta _ 0; IF i = 0 THEN start _ start + c.delta + (IF c.stemUp THEN Chord.Width[c] ELSE 0); IF i = 0 THEN x0 _ x0 + c.delta + (IF c.stemUp THEN Chord.Width[c] ELSE 0); a[i].x _ c.note[0].sync.time - start + c.delta; IF c.stemUp THEN a[i].x _ a[i].x + Chord.Width[c]; a[i].y _ y0 + b.tilt*a[i].x; a[i].value _ c.note[0].value; a[i].stemUp _ c.stemUp; a[i].dotted _ c.note[0].dotted; }; b: BeamPTR => { x: Time _ 0; WITH b.chord[0] SELECT FROM n: NotePTR => IF (a[i].stemUp _ n.stemUp) THEN x _ Note.Width[n]; c: ChordPTR => IF (a[i].stemUp _ c.stemUp) THEN x _ Chord.Width[c]; ENDCASE; IF i = 0 THEN start _ start +x; IF i = 0 THEN x0 _ x0 + x; a[i].x _ b.sync1.time - start+x; a[i].y _ y0 + b.tilt*a[i].x; a[i].value _ eighth; a[i].dotted _ FALSE; b.tilt _ b.tilt; b.staff _ b.staff; b.height _ Real.FixI[b.height+a[i].y-y0]; IF i+1 # b.length THEN LOOP; FOR j: CARDINAL IN [0..b.length) DO WITH b.chord[j] SELECT FROM -- find last note or chord in beam n: NotePTR => IF n.stemUp THEN x _ 8 ELSE x _ 0; c: ChordPTR => IF c.stemUp THEN x _ 8 ELSE x _ 0; ENDCASE; ENDLOOP; a[i].stemUp _ (x # 0); a[i].x _ b.sync2.time- start+ x; a[i].y _ y0 + b.tilt*a[i].x; }; ENDCASE; ENDLOOP; a[b.length].dotted _ FALSE; end _ b.length; <> i _ Sheet.FindLine[sheet, b.sync1.time]; lineHeight _ sheet.section[i].y-sheet.section[Sheet.NextLine[sheet, i]].y; FOR j IN [eighth..unknown) DO none _ TRUE; FOR i IN [0..end) DO IF none THEN IF a[i].value >= j THEN { none _ FALSE; s _ i; } ELSE LOOP; IF (i < end-1 AND j = eighth) OR (i < end-1 AND a[i+1].value >= j) THEN LOOP; up _ d _ 0; FOR k IN [s..i] DO IF a[k].stemUp THEN up _ up+1 ELSE d _ d+1; ENDLOOP; stemUp _ (up > d); xn _ a[i].x + x0; yn _ a[i].y; xi _ a[s].x + x0; none _ TRUE; IF xi = xn THEN SELECT TRUE FROM -- half beam i = 0 => {xn _ xn+10; yn _ yn+b.tilt*10}; i = end-1 => xi _ xi-10; a[i-1].value < a[i+1].value => {xn _ xn+10; yn _ yn+b.tilt*10}; a[i-1].value > a[i+1].value => xi _ xi-10; a[i+1].dotted => {xn _ xn+10; yn _ yn+b.tilt*10}; ENDCASE => xi _ xi-10; IF stemUp THEN yn _ yn - space*level ELSE yn _ yn + space*level; yi _ yn + b.tilt*(xi-xn); Utility.SetColor[sheet.context, IF inVoice THEN black ELSE light]; DrawClippedRect[sheet, xi, yi, xn-1, yn, overX, b.tilt, thickness, lineHeight]; FOR k IN [s..i] DO IF a[k].value # j THEN LOOP; IF a[k].stemUp = TRUE AND stemUp = FALSE THEN a[k].y _ a[k].y + space*level; IF a[k].stemUp = FALSE AND stemUp = TRUE THEN a[k].y _ a[k].y - space*level; yi _ a[k].y; IF a[k].stemUp AND a[k].x+x0 >= sheet.width+8 OR NOT a[k].stemUp AND a[k].x+x0 >= sheet.width THEN yi _ a[k].y - lineHeight; <> WITH b.chord[k] SELECT FROM n: NotePTR => Note.Draw[score, n, Real.FixI[yi]]; c: ChordPTR => Chord.Draw[score, c, Real.FixI[yi]]; b: BeamPTR => [] _ Beam.Draw[score, b]; ENDCASE; ENDLOOP; ENDLOOP; level _ level+1; ENDLOOP; IF b.ntuple # 0 AND ~(sheet.printing AND b.invisible) THEN { IF xn < x0 THEN xn _ xn+sheet.width; xi _ (x0+xn-10)/2; y0 _ Real.FixI[y0+b.tilt*(xi-x0)+(IF stemUp THEN 4 ELSE -12)]; IF xi > sheet.width THEN { xi _ xi-sheet.width; y0 _ y0-lineHeight; }; Imager.SetXY[sheet.context, [xi, y0]]; SELECT TRUE FROM b.invisible => Utility.SetColor[sheet.context, grey]; inVoice => Utility.SetColor[sheet.context, black]; ENDCASE => Utility.SetColor[sheet.context, light]; Utility.SetFont[sheet.context, text, 12]; Utility.DrawString[sheet.context, Convert.RopeFromInt[b.ntuple]]; Utility.SetFont[sheet.context, music, 8]; }; RETURN[i, i]; }; BeamArray: TYPE = ARRAY [0..32) OF BeamRecord; BeamRecord: TYPE = RECORD[value: NoteValue, stemUp, dotted: BOOL, x, y: REAL]; DrawNTuple: PROC[score: ScorePTR, b: BeamPTR] = { inVoice: BOOL; x, y, x1, y1: INTEGER; height1, height2: INTEGER; up0, upN: BOOL; sheet: SheetPTR _ score.sheet; IF sheet.printing AND b.invisible THEN RETURN; inVoice _ Beam.InVoice[b, score.sheet.voice]; height1 _ b.height+Sheet.Height[sheet, 0, , b.staff]; [x, y] _ Sheet.MapHeight[sheet, b.sync1.time, height1]; [x1, ] _ Sheet.MapHeight[sheet, b.sync2.time, height1]; y1 _ Real.FixI[y+ b.tilt*(x1+12-x)]; height2 _ Real.FixI[height1+y1-y]; Utility.SetColor[sheet.context, SELECT TRUE FROM b.invisible => grey, inVoice => black, ENDCASE => light]; DrawLine[sheet.context, x-2, y, x1+10, y1]; WITH b.chord[0] SELECT FROM n: NotePTR => up0 _ (Sheet.Height[sheet, n.sync.time, n.pitch, n.staff] > height1); c: ChordPTR => up0 _ (Sheet.Height[sheet, c.note[0].sync.time, c.note[0].pitch, c.note[0].staff] > height1); b: BeamPTR => up0 _ (b.height+Sheet.Height[sheet, b.sync1.time, , b.staff] > height1); ENDCASE; WITH b.chord[b.length-1] SELECT FROM n: NotePTR => upN _ (Sheet.Height[sheet, n.sync.time, n.pitch, n.staff] > height2); c: ChordPTR => upN _ (Sheet.Height[sheet, c.note[0].sync.time, c.note[0].pitch, c.note[0].staff] > height2); b: BeamPTR => upN _ (b.height+Sheet.Height[sheet, b.sync1.time, , b.staff] > height2); ENDCASE; IF up0 THEN DrawLine[sheet.context, x-2, y, x-2, y+4] ELSE DrawLine[sheet.context, x-2, y, x-2, y-4]; IF upN THEN DrawLine[sheet.context, x1+10, y1, x1+10, y1+4] ELSE DrawLine[sheet.context, x1+10, y1, x1+10, y1-4]; FOR i: CARDINAL IN [0..b.length) DO WITH b.chord[i] SELECT FROM n: NotePTR => Note.Draw[score, n]; c: ChordPTR => Chord.Draw[score, c]; b: BeamPTR => [] _ Beam.Draw[score, b]; ENDCASE; ENDLOOP; Imager.SetXY[sheet.context, [(x+x1)/2, y+b.tilt*(x1-x)/2+(IF up0 THEN -15 ELSE 5)]]; SELECT TRUE FROM b.invisible => Utility.SetColor[sheet.context, grey]; inVoice => Utility.SetColor[sheet.context, black]; ENDCASE => Utility.SetColor[sheet.context, light]; Utility.SetFont[sheet.context, text, 12]; Utility.DrawString[sheet.context, Convert.RopeFromInt[b.ntuple]]; Utility.SetFont[sheet.context, music, 8]; }; DrawPhysicalBeam: PROC[score: ScorePTR, b: BeamPTR] = { FOR i: CARDINAL IN [0..b.length) DO WITH b.chord[i] SELECT FROM n: NotePTR => Note.Draw[score, n]; c: ChordPTR => Chord.Draw[score, c]; b: BeamPTR => DrawPhysicalBeam[score, b]; ENDCASE; ENDLOOP; }; DrawClippedRect: PROC[sheet: SheetPTR, x0, y0, xn, yn, delta: REAL, tilt: REAL, thickness, lineHeight: INTEGER] = { path: Imager.Trajectory; xi: REAL _ xn; yi: REAL _ yn; clip: BOOL; clip _ x0 < sheet.width AND xn > sheet.width; IF clip THEN { xi _ sheet.width; yi _ y0 + tilt*(xi-x0); }; IF x0 > sheet.width THEN { y0 _ y0 - lineHeight; yi _ yi - lineHeight; x0 _ x0 - sheet.width+ delta; xi _ xi - sheet.width+ delta; }; path _ Imager.MoveTo[[x0, y0-thickness]]; path _ Imager.LineTo[path, [x0, y0+thickness]]; path _ Imager.LineTo[path, [xi+1, yi+thickness]]; path _ Imager.LineTo[path, [xi+1, yi-thickness]]; Imager.MaskFill[sheet.context, path]; IF NOT clip THEN RETURN; xi _ delta; xn _ xn - sheet.width+ delta; yn _ yn - lineHeight; yi _ yn - tilt*(xn-xi); path _ Imager.MoveTo[[xi, yi+thickness]]; path _ Imager.LineTo[path, [xi, yi-thickness]]; path _ Imager.LineTo[path, [xn+1, yn-thickness]]; path _ Imager.LineTo[path, [xn+1, yn+thickness]]; Imager.MaskFill[sheet.context, path]; }; Drawn: PUBLIC PROC[score: ScorePTR, b: BeamPTR] RETURNS[BOOL] = { cache: CachePTR ~ score.cache; IF b=NIL THEN { cache.beamQueue _ ALL[NIL]; RETURN[FALSE] }; FOR i: NAT IN[0..3) DO IF cache.beamQueue[i]=b THEN RETURN[TRUE] ENDLOOP; cache.beamIndex _ (cache.beamIndex+1) MOD 3; cache.beamQueue[cache.beamIndex] _ b; RETURN[FALSE]; }; END.