BeamImplB.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: November 8, 1983 9:40 am
Last Edited by: Maxwell, November 18, 1983 1:47 pm
Last Edited by: Doug Wyatt, June 16, 1984 3:30:01 pm PDT
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;
****************************************************************************
displaying beams
****************************************************************************
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: BOOLFALSE;
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: BOOLFALSE;
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
first we figure out where everything is going to be
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;
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 { 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;
IF ~sheet.printing AND AnyBug[] THEN RETURN[0, 0]; -- impatient user
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.