-- 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.