-- Author: John Maxwell -- last modified: November 8, 1983 9:40 am -- Last Edited by: Maxwell, November 18, 1983 1:47 pm DIRECTORY Beam USING [Draw, Grace, InVoice], Chord USING [Draw, Width], Graphics USING [DrawArea, LineTo, MoveTo, NewPath, Path, SetCP, SetStipple], MusicDefs, Note USING [Draw, Width], Real USING [FixI], Sheet USING [FindLine, Height, MapHeight, NextLine], String USING [AppendDecimal], Utility USING [DrawLine, DrawString, SetFont]; BeamImplB: PROGRAM IMPORTS Beam, Chord, Graphics, MusicDefs, Note, Real, Sheet, String, Utility EXPORTS Beam = BEGIN OPEN Graphics, MusicDefs, Utility; -- **************************************************************************** -- displaying beams -- **************************************************************************** Draw: PUBLIC PROCEDURE[score: ScorePTR, b: BeamPTR] RETURNS[INTEGER, INTEGER] = BEGIN start: Time; a: BeamArray; slope: REAL ← 5; i, end: CARDINAL; inVoice: BOOLEAN; sheet: SheetPTR ← score.sheet; nonGrace: BOOLEAN ← 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: BOOLEAN ← FALSE; IF NOT sheet.notehead THEN {DrawPhysicalBeam[score, b]; RETURN[0, 0]}; IF NOT b.beamed THEN BEGIN DrawNTuple[score, b]; RETURN[0, 0]; END; 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 -- first we figure out where everything is going to be FOR i: CARDINAL IN [0..b.length) DO WITH n: b.chord[i] SELECT FROM note => BEGIN IF sheet.display # graphical THEN n.n.delta ← 0; IF i = 0 THEN start ← start + n.n.delta + (IF n.n.stemUp THEN Note.Width[n.n] ELSE 0); IF i = 0 THEN x0 ← x0 + n.n.delta + (IF n.n.stemUp THEN Note.Width[n.n] ELSE 0); a[i].x ← n.n.sync.time - start + n.n.delta; IF n.n.stemUp THEN a[i].x ← a[i].x + Note.Width[n.n]; a[i].y ← y0 + b.tilt*a[i].x; a[i].value ← n.n.value; a[i].stemUp ← n.n.stemUp; a[i].dotted ← n.n.dotted; END; chord => BEGIN IF sheet.display # graphical THEN n.c.delta ← 0; IF i = 0 THEN start ← start + n.c.delta + (IF n.c.stemUp THEN Chord.Width[n.c] ELSE 0); IF i = 0 THEN x0 ← x0 + n.c.delta + (IF n.c.stemUp THEN Chord.Width[n.c] ELSE 0); a[i].x ← n.c.note[0].sync.time - start + n.c.delta; IF n.c.stemUp THEN a[i].x ← a[i].x + Chord.Width[n.c]; a[i].y ← y0 + b.tilt*a[i].x; a[i].value ← n.c.note[0].value; a[i].stemUp ← n.c.stemUp; a[i].dotted ← n.c.note[0].dotted; END; beam => BEGIN x: Time ← 0; WITH ev: n.b.chord[0] SELECT FROM note => IF (a[i].stemUp ← ev.n.stemUp) THEN x ← Note.Width[ev.n]; chord => IF (a[i].stemUp ← ev.c.stemUp) THEN x ← Chord.Width[ev.c]; ENDCASE; IF i = 0 THEN start ← start +x; IF i = 0 THEN x0 ← x0 + x; a[i].x ← n.b.sync1.time - start+x; a[i].y ← y0 + b.tilt*a[i].x; a[i].value ← eighth; a[i].dotted ← FALSE; n.b.tilt ← b.tilt; n.b.staff ← b.staff; n.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 ev: n.b.chord[j] SELECT FROM -- find last note or chord in beam note => IF ev.n.stemUp THEN x ← 8 ELSE x ← 0; chord => IF ev.c.stemUp THEN x ← 8 ELSE x ← 0; ENDCASE; ENDLOOP; a[i].stemUp ← (x # 0); a[i].x ← n.b.sync2.time- start+ x; a[i].y ← y0 + b.tilt*a[i].x; END; ENDCASE; ENDLOOP; a[b.length].dotted ← FALSE; end ← b.length; -- then we draw the beams and components 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 BEGIN none ← FALSE; s ← i; END 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); IF inVoice THEN Graphics.SetStipple[sheet.context, black] ELSE Graphics.SetStipple[sheet.context, 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; IF ~sheet.printing AND AnyBug[] THEN RETURN[0, 0]; -- impatient user WITH ev: b.chord[k] SELECT FROM note => Note.Draw[score, ev.n, Real.FixI[yi]]; chord => Chord.Draw[score, ev.c, Real.FixI[yi]]; beam => [] ← Beam.Draw[score, ev.b]; ENDCASE; ENDLOOP; ENDLOOP; level ← level+1; ENDLOOP; IF b.ntuple # 0 AND ~(sheet.printing AND b.invisible) THEN BEGIN string: STRING ← [10]; 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 BEGIN xi ← xi-sheet.width; y0 ← y0-lineHeight; END; SetCP[sheet.context, xi, y0]; String.AppendDecimal[string, b.ntuple]; SELECT TRUE FROM b.invisible => Graphics.SetStipple[sheet.context, grey]; inVoice => Graphics.SetStipple[sheet.context, black]; ENDCASE => Graphics.SetStipple[sheet.context, light]; SetFont[sheet.context, text, 12]; DrawString[sheet.context, string]; SetFont[sheet.context, music, 8]; END; RETURN[i, i]; END; BeamArray: TYPE = ARRAY [0..32) OF BeamRecord; BeamRecord: TYPE = RECORD[value: NoteValue, stemUp, dotted: BOOLEAN, x, y: REAL]; DrawNTuple: PROCEDURE[score: ScorePTR, b: BeamPTR] = BEGIN inVoice: BOOLEAN; x, y, x1, y1: INTEGER; height1, height2: INTEGER; up0, upN: BOOLEAN; string: STRING ← [10]; 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]; SELECT TRUE FROM b.invisible => Graphics.SetStipple[sheet.context, grey]; inVoice => Graphics.SetStipple[sheet.context, black]; ENDCASE => Graphics.SetStipple[sheet.context, light]; DrawLine[sheet.context, x-2, y, x1+10, y1]; WITH n: b.chord[0] SELECT FROM note => up0 ← (Sheet.Height[sheet, n.n.sync.time, n.n.pitch, n.n.staff] > height1); chord => up0 ← (Sheet.Height[sheet, n.c.note[0].sync.time, n.c.note[0].pitch, n.c.note[0].staff] > height1); beam => up0 ← (n.b.height+Sheet.Height[sheet, n.b.sync1.time, , n.b.staff] > height1); ENDCASE; WITH n: b.chord[b.length-1] SELECT FROM note => upN ← (Sheet.Height[sheet, n.n.sync.time, n.n.pitch, n.n.staff] > height2); chord => upN ← (Sheet.Height[sheet, n.c.note[0].sync.time, n.c.note[0].pitch, n.c.note[0].staff] > height2); beam => upN ← (n.b.height+Sheet.Height[sheet, n.b.sync1.time, , n.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 n: b.chord[i] SELECT FROM note => Note.Draw[score, n.n]; chord => Chord.Draw[score, n.c]; beam => [] ← Beam.Draw[score, n.b]; ENDCASE; ENDLOOP; SetCP[sheet.context, (x+x1)/2, y+b.tilt*(x1-x)/2+(IF up0 THEN -15 ELSE 5)]; SELECT TRUE FROM b.invisible => Graphics.SetStipple[sheet.context, grey]; inVoice => Graphics.SetStipple[sheet.context, black]; ENDCASE => Graphics.SetStipple[sheet.context, light]; String.AppendDecimal[string, b.ntuple]; SetFont[sheet.context, text, 12]; DrawString[sheet.context, string]; SetFont[sheet.context, music, 8]; END; DrawPhysicalBeam: PROCEDURE[score: ScorePTR, b: BeamPTR] = BEGIN FOR i: CARDINAL IN [0..b.length) DO WITH ev: b.chord[i] SELECT FROM note => Note.Draw[score, ev.n]; chord => Chord.Draw[score, ev.c]; beam => DrawPhysicalBeam[score, ev.b]; ENDCASE; ENDLOOP; END; DrawClippedRect: PROCEDURE[sheet: SheetPTR, x0, y0, xn, yn, delta: REAL, tilt: REAL, thickness, lineHeight: INTEGER] = BEGIN path: Path; xi: REAL ← xn; yi: REAL ← yn; clip: BOOLEAN; clip ← x0 < sheet.width AND xn > sheet.width; IF clip THEN BEGIN xi ← sheet.width; yi ← y0 + tilt*(xi-x0); END; IF x0 > sheet.width THEN BEGIN y0 ← y0 - lineHeight; yi ← yi - lineHeight; x0 ← x0 - sheet.width+ delta; xi ← xi - sheet.width+ delta; END; path ← NewPath[4]; MoveTo[path, x0, y0-thickness]; LineTo[path, x0, y0+thickness]; LineTo[path, xi+1, yi+thickness]; LineTo[path, xi+1, yi-thickness]; LineTo[path, x0, y0-thickness]; DrawArea[sheet.context, path]; IF NOT clip THEN RETURN; xi ← delta; xn ← xn - sheet.width+ delta; yn ← yn - lineHeight; yi ← yn - tilt*(xn-xi); MoveTo[path, xi, yi +thickness]; LineTo[path, xi, yi-thickness]; LineTo[path, xn+1, yn-thickness]; LineTo[path, xn+1, yn+thickness]; LineTo[path, xi, yi +thickness]; DrawArea[sheet.context, path]; END; Drawn: PUBLIC PROCEDURE[score: ScorePTR, b: BeamPTR] RETURNS[BOOLEAN] = BEGIN i: CARDINAL; IF b = NIL THEN score.cache.beamQueueLength ← 0; IF b = NIL THEN RETURN[FALSE]; FOR i IN [0..score.cache.beamQueueLength) DO IF score.cache.beamQueue[i] = b THEN RETURN[TRUE]; ENDLOOP; score.cache.beamQueueLength ← score.cache.beamQueueLength+1; IF score.cache.beamQueueLength > 3 THEN score.cache.beamQueueLength ← 0; score.cache.beamQueue[score.cache.beamQueueLength] ← b; RETURN[FALSE]; END; END.