<* PRAGMA LL *> MODULEEuclidView EXPORTSEuclid ; IMPORT Animate, Axis, EuclidViewClass, Filter, Font, Math, Matrix2D, MG, MGPublic, MGV, PaintOp, Pts, R2, R2Path, Region, ScaleFilter, Thread, VBT, View, ZeusPanel; TYPE T = EuclidViewClass.T BRANDED OBJECT v : V; a : REAL := 3.0; b : REAL := 4.0; pts : ARRAY [A .. X] OF MG.T; lines: ARRAY [A .. X], [A .. X] OF MG.T; stash: ARRAY [0 .. 20] OF RECORD p1, p2, p3, p4: Id; mg : MG.T END; OVERRIDES startrun := StartRun; <* LL=0 *> oeSetupTriangle := SetupTriangle; oeSetupSquare := SetupSquare; oeLineAL := LineAL; oeTriangle := Triangle; oeQuad := Quad; oeShear := Shear; oeRotateTriangle := RotateTriangle; oeRotateAngle := RotateAngle; oeRotateLine := RotateLine; oeHighlightLine := HighlightLine; oeHighlightAngle := HighlightAngle; oeRemove := Remove; END; V = MGV.V OBJECT OVERRIDES shape := ShapeV; END; VAR lineColor := ARRAY [0 .. 2] OF PaintOp.ColorScheme{ PaintOp.bgFg, MGPublic.ColorFromText("Green", "Green"), MGPublic.ColorFromText("Red", "Red")}; PROCEDUREShapeV (v: V; ax: Axis.T; <* UNUSED *> n: CARDINAL): VBT.SizeRange = VAR t : T := v.view; size: REAL; BEGIN IF t # NIL THEN IF ax = Axis.T.Hor THEN size := t.a + t.b + t.b ELSE size := t.a + t.a + t.b END; ELSE IF ax = Axis.T.Hor THEN size := 11.0 ELSE size := 10.0 END; END; WITH pref = Pts.ToScreenPixels(v, EScale * 1.4 * size, ax) DO RETURN VBT.SizeRange{pref, pref, MAX(pref + 1, VBT.DefaultShape.hi)} END; END ShapeV; VAR font := Font.FromName( ARRAY OF TEXT{"-*-helvetica-bold-r-*-*-*-100-*-*-*-*-iso8859-1"}); CONST EScale = 20.0; CONST Label = ARRAY OF TEXT{"A", "B", "C", "D", "E", "F", "G", "H", "K", "L", ""}; PROCEDUREInitPt (t: T; id: Id; setLabel := TRUE) = VAR a := t.a; b := t.b; r := (a * a) / (a * a + b * b); BEGIN IF t.pts[id] = NIL THEN CASE id OF | A => Pt(t, A, b, a, MG.Alignment.SW); | B => Pt(t, B, b + a, a, MG.Alignment.SE); | C => Pt(t, C, b, a + b, MG.Alignment.NW); | D => Pt(t, D, a + b + b, a + a, MG.Alignment.E); | E => Pt(t, E, b + b, a + a + b, MG.Alignment.NE); | F => Pt(t, F, a + b, 0.0, MG.Alignment.SE); | G => Pt(t, G, b, 0.0, MG.Alignment.SW); | H => Pt(t, H, 0.0, a, MG.Alignment.SW); | K => Pt(t, K, 0.0, a + b, MG.Alignment.NW); | L => Pt(t, L, a + b + b - r * a, a + a + r * b, MG.Alignment.NE); | X => Pt(t, X, a + b - r * a, a + r * b, MG.Alignment.NE); ELSE <* ASSERT FALSE *> END; END; IF setLabel THEN t.pts[id].setLabel(t.v, Label[id]); END; END InitPt; PROCEDUREPt (t: T; id: Id; h, v: REAL; dir: MG.Alignment) = BEGIN t.pts[id] := NEW(MG.Label, font := font, alignment := dir, m := Matrix2D.Translate( EScale * (2.0 + h), EScale * (2.0 + v))).init( v := t.v) END Pt; CONST Weight = 2.0; PROCEDURENewLine (t : T; p1, p2: Id; weight: REAL; color : PaintOp.ColorScheme): MG.Line = BEGIN RETURN NEW(MG.Line, weight := weight, color := color).init( from := MGPublic.Pos(t.pts[p1], t.v), to := MGPublic.Pos(t.pts[p2], t.v), v := t.v); END NewLine; PROCEDURELine (t: T; p1, p2: Id) = BEGIN IF t.lines[p1, p2] = NIL THEN t.lines[p1, p2] := NewLine(t, p1, p2, Weight, PaintOp.bgFg); t.lines[p2, p1] := t.lines[p1, p2]; END; END Line; PROCEDURELineAL (t: T; show: BOOLEAN) = BEGIN InitPt(t, L); Line(t, A, L); IF show THEN t.v.mgRedisplay(Region.Full) END; END LineAL; VAR objColor := ARRAY [1..2] OF PaintOp.ColorScheme{ MGPublic.ColorFromText("VeryLightBlue", "Black"), MGPublic.ColorFromText("VeryLightRed", "Black") }; PROCEDUREMakeTriangle (t: T; p1, p2, p3: Id; color: INTEGER): MG.Shape = VAR path := NEW(R2Path.T); shape: MG.Shape; BEGIN path.init(); path.moveTo(MGPublic.Pos(t.pts[p1], t.v)); path.lineTo(MGPublic.Pos(t.pts[p2], t.v)); path.lineTo(MGPublic.Pos(t.pts[p3], t.v)); path.close(); shape := NEW(MG.Shape, color := objColor[color]).init(R2.Origin, path, v := t.v); AddToStash(t, shape, p1, p2, p3); RETURN shape; END MakeTriangle; PROCEDURERemove (t: T; p1, p2, p3, p4: Id; show: BOOLEAN) = BEGIN RemoveFromStash(t, p1, p2, p3, p4); IF show THEN t.v.mgRedisplay(Region.Full) END; END Remove; PROCEDURETriangle (t: T; p1, p2, p3: Id; color: INTEGER) = BEGIN EVAL MakeTriangle(t, p1, p2, p3, color); t.v.mgRedisplay(Region.Full); END Triangle; PROCEDUREQuad (t: T; p1, p2, p3, p4: Id; color: INTEGER) = VAR path : R2Path.T; shape: MG.Shape; BEGIN InitPt(t, p1, FALSE); InitPt(t, p2, FALSE); InitPt(t, p3, FALSE); InitPt(t, p4, FALSE); path := NEW(R2Path.T); path.init(); path.moveTo(MGPublic.Pos(t.pts[p1], t.v)); path.lineTo(MGPublic.Pos(t.pts[p2], t.v)); path.lineTo(MGPublic.Pos(t.pts[p3], t.v)); path.lineTo(MGPublic.Pos(t.pts[p4], t.v)); path.close(); shape := NEW(MG.Shape, color := objColor[color]).init(R2.Origin, path, v := t.v); AddToStash(t, shape, p1, p2, p3, p4); t.v.mgRedisplay(Region.Full); END Quad; TYPE ShearAnimation = Animate.T OBJECT p1, p2, from, to: R2.T; OVERRIDES doStep := ShearStep; END; PROCEDUREShearStep ( t : ShearAnimation; time : REAL; <* UNUSED *> timePrev: REAL; v : MG.V; mg : MG.T ) = VAR path := NEW(R2Path.T); BEGIN path.init(); path.moveTo(t.p1); path.lineTo(t.p2); path.lineTo(R2.Add(t.from, R2.Scale(time, R2.Sub(t.to, t.from)))); path.close(); NARROW(mg, MG.Shape).reshape(v, R2.Origin, path); END ShearStep; PROCEDUREShear (t: T; p1, p2, from, to: Id) RAISES {Thread.Alerted} = VAR ptFrom := MGPublic.Pos(t.pts[from], t.v); ptTo := MGPublic.Pos(t.pts[to], t.v); triangle := Stash(t, from, p1, p2); BEGIN LOCK t.v.mu DO t.v.displayList.top(t.v, t.lines[p1, p2]); END; MGV.AddAnimation( t.v, NEW(ShearAnimation, from := ptFrom, to := ptTo, p1 := MGPublic.Pos(t.pts[p1], t.v), p2 := MGPublic.Pos(t.pts[p2], t.v)).init(), triangle); MGV.Animation(t.v); END Shear; CONST TwoPi = FLOAT(2.0D0 * Math.Pi, REAL); PROCEDURERotationAngle (t: T; pivot, pFrom: Id; pTo: Id): REAL = VAR ptFrom := MGPublic.Pos(t.pts[pFrom], t.v); ptTo := MGPublic.Pos(t.pts[pTo], t.v); ptPiv := MGPublic.Pos(t.pts[pivot], t.v); angle1 := Angle(R2.Sub(ptFrom, ptPiv)); angle2 := Angle(R2.Sub(ptTo, ptPiv)); ang := angle2 - angle1; BEGIN
IF ang < 0.0 THEN ang := ang + TwoPi; ELSIF ang > TwoPi THEN ang := ang - TwoPi; END; IF ang > Math.Pi THEN ang := TwoPi - ang; END;
RETURN ang;
END RotationAngle;
PROCEDURE RotateLine (t: T; pivot, pFrom: Id; pTo: Id)
RAISES {Thread.Alerted} =
VAR
line := Stash(t, pivot, pFrom);
ang := RotationAngle(t, pivot, pFrom, pTo);
BEGIN
MGV.AddAnimation(
t.v, NEW(Animate.Rotate, origin := MGPublic.Pos(t.pts[pivot], t.v),
angle := ang / FLOAT(Math.Degree, REAL)).init(), line);
MGV.Animation(t.v);
END RotateLine;
PROCEDURE RotateTriangle ( t : T;
pivot, p1From: Id;
p1To : Id;
p2From : Id;
<* UNUSED *> p2To : Id )
RAISES {Thread.Alerted} =
VAR
triangle := Stash(t, pivot, p1From, p2From);
ang := RotationAngle(t, pivot, p1From, p1To);
BEGIN
MGV.AddAnimation(
t.v, NEW(Animate.Rotate, origin := MGPublic.Pos(t.pts[pivot], t.v),
angle := ang / FLOAT(Math.Degree, REAL)).init(), triangle);
MGV.Animation(t.v);
END RotateTriangle;
PROCEDURE RotateAngle ( t : T;
pivot, p1From: Id;
p1To : Id;
p2From : Id;
<* UNUSED *> p2To : Id )
RAISES {Thread.Alerted} =
VAR
angle := Stash(t, p1From, pivot, p2From);
ang := RotationAngle(t, pivot, p1From, p1To);
BEGIN
MGV.AddAnimation(
t.v, NEW(Animate.Rotate, origin := MGPublic.Pos(t.pts[pivot], t.v),
angle := ang / FLOAT(Math.Degree, REAL)).init(), angle);
MGV.Animation(t.v);
END RotateAngle;
PROCEDURE HighlightLine (t: T; p1, p2: Id; color: INTEGER; show: BOOLEAN)
RAISES {Thread.Alerted} =
VAR
line := NewLine(t, p1, p2, 2.0 * Weight, lineColor[color]);
BEGIN
AddToStash(t, line, p1, p2);
IF show THEN MGV.Animation(t.v) END;
END HighlightLine;
PROCEDURE AddToStash (t: T; mg: MG.T; p1, p2, p3, p4: Id := -1) =
BEGIN
FOR i := 0 TO LAST(t.stash) DO
WITH e = t.stash[i] DO
IF e.mg = NIL THEN
e.p1 := p1;
e.p2 := p2;
e.p3 := p3;
e.p4 := p4;
e.mg := mg;
RETURN
END;
END;
END;
END AddToStash;
PROCEDURE RemoveFromStash (t: T; p1, p2, p3, p4: Id := -1) =
BEGIN
FOR i := 0 TO LAST(t.stash) DO
WITH e = t.stash[i] DO
IF e.p1 = p1 AND e.p2 = p2 AND e.p3 = p3 AND e.p4 = p4 THEN
t.v.displayList.remove(t.v, e.mg);
e.mg := NIL;
RETURN
END;
END;
END;
END RemoveFromStash;
PROCEDURE Stash (t: T; p1, p2, p3, p4: Id := -1): MG.T =
BEGIN
FOR i := 0 TO LAST(t.stash) DO
WITH e = t.stash[i] DO
IF e.p1 = p1 AND e.p2 = p2 AND e.p3 = p3 AND e.p4 = p4 THEN
RETURN e.mg
END;
END;
END;
<* ASSERT FALSE *>
END Stash;
PROCEDURE Angle (pt: R2.T): REAL =
VAR
l := R2.Length(pt);
angle := FLOAT(Math.acos(FLOAT(pt[0] / l, LONGREAL)));
BEGIN
IF pt[1] < 0.0 THEN angle := TwoPi - angle; END;
RETURN angle;
END Angle;
PROCEDURE HighlightAngle (t : T;
p1, vertex, p2: Id;
value : INTEGER;
show : BOOLEAN )
RAISES {Thread.Alerted} =
VAR
pt1 := MGPublic.Pos(t.pts[p1], t.v);
ptV := MGPublic.Pos(t.pts[vertex], t.v);
pt2 := MGPublic.Pos(t.pts[p2], t.v);
path: R2Path.T;
r : REAL;
v1 := R2.Sub(pt1, ptV);
BEGIN
path := NEW(R2Path.T);
path.init();
r := EScale;
path.moveTo(R2.Add(ptV, R2.Scale(r / R2.Length(v1), v1)));
path.arcTo(ptV, r, Angle(v1), Angle(R2.Sub(pt2, ptV)));
AddToStash(t, NEW(MG.Shape, weight := 2.0 * Weight,
color := lineColor[value]).init(
R2.Origin, path, FALSE, v := t.v), p1, vertex, p2);
IF show THEN MGV.Animation(t.v) END;
END HighlightAngle;
PROCEDURE SetupTriangle (t: T; a, b: REAL) =
VAR v := NEW(V, border := ARRAY Axis.T OF REAL{0.0, 0.0}).init();
BEGIN
LOCK VBT.mu DO EVAL Filter.Replace(t, NEW(ScaleFilter.T).init(v)) END;
t.v := v;
v.view := t;
FOR i := 0 TO LAST(t.pts) DO
t.pts[i] := NIL;
FOR j := 0 TO LAST(t.lines[i]) DO t.lines[i, j] := NIL; END;
END;
FOR i := 0 TO LAST(t.stash) DO t.stash[i].mg := NIL END;
t.a := a;
t.b := b;
InitPt(t, A);
InitPt(t, B);
InitPt(t, C);
Line(t, A, B);
Line(t, C, B);
Line(t, A, C);
t.v.mgRedisplay(Region.Full);
END SetupTriangle;
PROCEDURE SetupSquare (t: T; p1, p2, p3, p4: Id) =
BEGIN
InitPt(t, p1);
InitPt(t, p2);
InitPt(t, p3);
InitPt(t, p4);
Line(t, p1, p2);
Line(t, p2, p3);
Line(t, p3, p4);
Line(t, p4, p1);
t.v.mgRedisplay(Region.Full);
END SetupSquare;
PROCEDURE StartRun (view: T) =
BEGIN
EVAL Filter.Replace(view, NEW(V).init())
END StartRun;
PROCEDURE New (): View.T =
BEGIN
RETURN NEW(T).init(NEW(V).init())
END New;
BEGIN
ZeusPanel.RegisterView (New, "Euclid View", "Euclid");
END EuclidView.