MODULE Jumble; (* A module for drawing the "Jumble" puzzles found on the comics page of many newspapers. *) IMPORT PS, Text, R2, Geometry, Rel, Rect, TypeLinesC, Circle; PRIVATE VAR ThinWidth, ThickWidth; /* initialized by the "BlockSize" procedure */ PRIVATE PROC sz := BlockSize() IS VAR asc, dec IN asc, dec := PS.FontHeight(); sz := 1.05 * (asc + dec); ThinWidth := sz / 12; ThickWidth := ThinWidth * 2 END END; PRIVATE PROC DrawNE(p, q) IS SAVE PS IN VAR d1, d2 IN d1 := ThinWidth; d2 := ThickWidth / 2; p := R2.Plus(p, (-d1, d2)); q := R2.Plus(q, (d2, -d1)); VAR ne IN ne := Geometry.HorVer(p, q); PS.MoveTo(p); PS.LineTo(ne); PS.LineTo(q); PS.SetWidth(ThickWidth); PS.Stroke() END END END END; PRIVATE PROC DrawBars(r, sz, len) IS IF VAR i = 1, delta = sz + ThinWidth, sw, ne IN r = (sw, ne) -> DO i < len -> VAR n IN n := R2.MinusX(ne, i * delta); PS.MoveTo(n); PS.LineTo(Geometry.HorVer(sw, n)); PS.Stroke() END; i := i + 1 OD END FI END; PRIVATE PROC DrawBoxes(q, sz, len) IS VAR ne, sw, inset IN ne := R2.PlusX(q, 2 * sz + 3 * ThinWidth); sw := R2.Minus(ne, (len * sz + (len + 1) * ThinWidth, sz + 2 * ThinWidth)); inset := Rect.Inset((sw, ne), ThinWidth / 2); SAVE PS IN Rect.DrawR(inset); PS.SetWidth(ThinWidth); PS.Stroke(); DrawBars(inset, sz, len); SAVE PS IN PS.MoveTo(q); PS.LineTo(ne); PS.SetWidth(ThickWidth); PS.Stroke() END END END END; PRIVATE CONST VertBar = Text.GetChar("|", 0); PRIVATE PROC len := WordLen(txt) IS len := Text.Length(txt); IF Text.FindChar(txt, VertBar) = len - 1 -> len := len - 1 | SKIP FI END; /* Return the length of "txt", not counting a trailing vertical bar character. */ PROC Word(p, txt) IS SAVE PS IN VAR sz, q IN sz := BlockSize(); q := R2.Plus(p, (4 * sz + 3 * ThinWidth, -sz)); PS.SetFontFace("Helvetica-Bold"); TypeLinesC.Center(Geometry.Mid(p, q), txt); Rect.DrawR(Rect.Inset((p, q), -ThinWidth / 2)); PS.SetWidth(ThinWidth); PS.Stroke(); DrawNE(p, q); DrawBoxes(q, sz, WordLen(txt)) END END END; UI TextTool(Word); (* Draw a Jumble block with northwest corner at "p" containing the jumbled letters "txt". *) PROC CircleChar(a, p) IS SAVE PS IN VAR sz, d, b, q IN sz := BlockSize(); d := sz / 2; a := R2.Plus(a, (d, -d)); b := R2.PlusX(a, sz + ThinWidth); q := Rel.Inv(p, a, b); q := (ROUND(CAR(q)), ROUND(CDR(q))) REL (a, b); Circle.Draw(q, R2.PlusX(q, (sz + ThinWidth) / 2)); PS.SetWidth(ThinWidth); PS.Stroke() END END END; UI PointTool(CircleChar); (* Draw a circle in the box containing the point "q" for the jumble drawn with control point "p". *) PROC Cmd0() IS IF VAR a ~ (-100.7, 104.7), b = (-102.2, 191.9), c ~ (-102.2, 116.9), d ~ (-102.2, 41.85), e ~ (-102.2, -33.17), g ~ (1.515, 155.5), h ~ (28.02, 155.5), i ~ (-67.41, 79.64), j ~ (-41.66, 81.16), k ~ (-90.89, 6.826), l ~ (-68.92, 5.309), m ~ (-91.64, -69.02), n ~ (-40.14, -72.81), o ~ (-15.15, -69.78) IN b VER c AND b VER d AND b VER e AND Geometry.CongY(b, c, c, d) AND Geometry.CongY(b, c, d, e) -> PS.SetFontSize(PS.Large); Word(b, "TACUE"); Word(c, "CINEW"); Word(d, "GREEME"); Word(e, "LEHTAH"); CircleChar(b, g); CircleChar(b, h); CircleChar(c, i); CircleChar(c, j); CircleChar(d, k); CircleChar(d, l); CircleChar(e, m); CircleChar(e, n); CircleChar(e, o) END FI END;