MODULE Spirograph; CONST small = 0.01; VAR anim := 0, delta := 0.1, DarkGreen := Color.FromRGB(0, 0.2, 0); PRIVATE PROC res := Abs(x) IS x < 0 -> res := -x | res := x END; PRIVATE PROC res := GCD(a, b) IS (Abs(a - b) < small) -> res := a | a > b -> res := GCD(a - b, b) | res := GCD(a, b - a) END; PROC res := Pnt(c, rad, holeRad, outer, inner, t, initAng) IS VAR centerRad, innerRad, innerAngle IN centerRad := rad * (outer - inner) / outer; innerRad := rad * inner / outer - holeRad; innerAngle := t - (t - initAng) * outer / inner; res := (CAR(c) + centerRad * Math.Cos(t) + innerRad * Math.Cos(innerAngle), CDR(c) + centerRad * Math.Sin(t) + innerRad * Math.Sin(innerAngle)) END END; (* "Draw(c, rad, holeRad, outer, inner, initAng)" draws a spirograph figure in an outer ring centered at "c" with radius "rad" points. The outer wheel is assumed to have "outer" gear teeth (should be an integer) and the inner ring to have "inner" gear teeth; the radii of the outer and inner circles is assumed to be in the same ratio as their teeth. The pen is "holeRad" points from the circumference of the inner wheel, and the drawing starts with the inner and outer wheels tangent "initAng" degrees clockwise degrees from the vertical, and the pen along the line between the centers of the wheels and this point of tangency. *) PROC Draw(c, rad, holeRad, outer, inner, initAng) IS VAR maxAng, t IN initAng := 90 - initAng; initAng := initAng / 360 * 2 * Math.Pi; t := initAng; maxAng := 2 * Math.Pi * inner / GCD(outer, inner) + initAng; PS.NewPath(); PS.MoveTo(Pnt(c, rad, holeRad, outer, inner, t, initAng)); DO t < maxAng -> t := t + delta; VAR newPt IN newPt := Pnt(c, rad, holeRad, outer, inner, t, initAng); PS.LineTo(newPt); IF anim = 1 -> PS.Stroke(); PS.ShowPage(); PS.MoveTo(newPt) | SKIP FI END OD; IF anim # 1 -> PS.Stroke() | SKIP FI END END; PROC DrawN(c, rad, holeRad, outer, inner, initAng, n, degInc) IS VAR i IN i := 0; DO i + small < n -> Draw(c, rad, holeRad, outer, inner, initAng + i * degInc); i := i + 1 OD END END; PROC Demo1(a) IS PS.SetColor(DarkGreen); Draw(a, 150, 20, 12, 5, 0); PS.SetColor(Color.Blue); Draw(a, 150, 20, 12, 7, 0); PS.SetColor(Color.Red); Draw(a, 150, 20, 12, 11, 0) END; UI PointTool(Demo1); PROC Demo2(a) IS PS.SetColor(DarkGreen); Draw(a, 150, 10, 36, 7, 0); PS.SetColor(Color.Magenta); Draw(a, 150, 10, 36, 5, 0); PS.SetColor(Color.Blue); Draw(a, 150, 10, 36, 13, 0); PS.SetColor(Color.Red); Draw(a, 150, 10, 36, 23, 0) END; UI PointTool(Demo2); PROC Demo3(a) IS PS.SetColor(DarkGreen); DrawN(a, 150, 20, 33, 21, 0, 4, 360 / 11 / 4); PS.SetColor(Color.Red); DrawN(a, 150, 20, 33, 27, 0, 4, 360 / 11 / 4); PS.SetColor(Color.Cyan); DrawN(a, 130, 0, 33, 9, 0, 4, 360 / 11 / 4) END; UI PointTool(Demo3); PROC Cmd0() IS IF VAR a ~ (-107.5, 231.3), b ~ (87.1, 12.89), c ~ (-101.5, -230.6) IN Demo1(a); Demo2(b); Demo3(c) END FI END;