############################################################################ # # File: bevel.icn # # Subject: Procedures for drawing beveled objects # # Author: Gregg M. Townsend # # Date: April 1, 1997 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # These procedures draw objects having a raised or sunken appearance. # # BevelReset(win) sets/resets shading colors. # # BevelCircle(win, x, y, r, bw) draws a beveled circle. # # BevelDiamond(win, x, y, r, bw) draws a beveled diamond. # # BevelTriangle(win, x, y, r, o, bw) draws a beveled triangle. # # BevelSquare(win, x, y, r, bw) draws a beveled square. # # FillSquare(win, x, y, r) fills a square. # # FillDiamond(win, x, y, r) fills a diamond. # # FillTriangle(win, x, y, r, o) fills a triangle. # # RidgeRectangle(win, x, y, w, h, bw) draws a ridged rectangle. # # GrooveRectangle(win, x, y, w, h, bw) draws a grooved rectangle. # # BevelRectangle(win, x, y, w, h, bw) draws a beveled rectangle. # # DrawRidge(win, x1, y1, x2, y2, w) draws a ridged line. # # DrawGroove(win, x1, y1, x2, y2, w) draws a grooved line. # ############################################################################ # # These procedures allow the drawing of buttons and other objects # with a three-dimensional appearance. They are intended to be # used like other graphics primitives (DrawRectangle() etc.). # However, this abstraction fails if the background color changes # or if clipping is set, due to the use of cached graphics contexts. # # BevelReset(win) -- set/reset colors for beveling # This procedure is called automatically by the others. # It can be called explicitly if the background color is changed. # # BevelCircle(win, x, y, r, bw) -- draw beveled circle # BevelDiamond(win, x, y, r, bw) -- draw beveled diamond # BevelTriangle(win, x, y, r, o, bw) -- draw beveled triangle # BevelSquare(win, x, y, r, bw) -- draw beveled square # These procedures draw a figure centered at (x,y) and having # a "radius" of r. bw is the bevel width, in pixels. # o is the triangle orientation: "n", "s", "e", or "w". # # FillSquare(win, x, y, r) -- fill square centered at (x,y) # FillDiamond(win, x, y, r) -- fill diamond centered at (x,y) # FillTriangle(win, x, y, r, o) -- fill triangle centered at (x,y) # These procedures complement the beveled outline procedures # by filling a figure centered at (x,y). Fillcircle is already # an Icon function and so is not included here. # # RidgeRectangle(win, x, y, w, h, bw) -- draw ridged rectangle # GrooveRectangle(win, x, y, w, h, bw) -- draw grooved rectangle # BevelRectangle(win, x, y, w, h, bw) -- draw beveled rectangle # These procedures draw a rectangle with the given external # dimensions and border width. Beveled rectangles are raised # if bw > 0 or sunken if bw < 0. # # DrawRidge(win, x1, y1, x2, y2, w) -- draw a ridged line # DrawGroove(win, x1, y1, x2, y2, w) -- draw a grooved line # These procedures draw a groove or ridge of width 2 at any angle. # If w = 0, a groove or ridge is erased to the background color. # # For BevelSquare() and FillSquare(), the width drawn is 2 * r + 1, # not just 2 * r. This is necessary to keep the visual center at the # specified (x, y) and is consistent with the other centered procedures # and the built-in function FillCircle. # ############################################################################ # # Includes: vdefns # ############################################################################ # # Links: graphics # ############################################################################ # # Requires: Version 9 graphics # ############################################################################ $include "vdefns.icn" link graphics global bev_table record bev_record(shadow, hilite) # BevelReset(win) -- set/reset colors for beveling # # Called automatically the first time a beveling procedure is called; # must also be called explicitly if the background color is changed. # (Pale, weak background colors work best with beveling.) procedure BevelReset(win) #: set colors for beveled drawing local b, h, l, s, hilite, shadow, lhilite, lshadow /win := &window /bev_table := table() if b := \bev_table[win] then { Uncouple(b.hilite) Uncouple(b.shadow) b := &null } if WAttrib(win, "depth") >= 4 then { HLS(ColorValue(Bg(win))) ? { h := tab(many(&digits)) move(1) l := tab(many(&digits)) move(1) s := tab(0) } case l of { 0 <= l < 10 & l: { lshadow := 25; lhilite := 50 } 10 <= l < 25 & l: { lshadow := 0; lhilite := l + 25 } 25 <= l < 75 & l: { lshadow := l - 25; lhilite := l + 25 } 75 <= l < 90 & l: { lshadow := l - 25; lhilite := 100 } default: { lshadow := 50; lhilite := 75 } } s /:= 2 shadow := Clone(win, "fg=" || HLSValue(h || ":" || lshadow || ":" || s), "linewidth=1", "linestyle=solid", "fillstyle=solid", "drawop=copy") hilite := Clone(shadow, "fg=" || HLSValue(h || ":" || lhilite || ":" || s)) b := bev_record(\shadow, \hilite) } if /b then { shadow := Clone(win, "linewidth=1", "linestyle=solid", "fillstyle=solid", "drawop=copy") hilite := Clone(shadow, "fillstyle=textured", "pattern=gray") b := bev_record(shadow, hilite) } bev_table[win] := bev_record(shadow, hilite) return win end # bev_lookup(win) -- look up and return bev_record for a window. # # (Internal procedure) procedure bev_lookup(win) local b, dx, dy b := \(\bev_table)[win] | bev_table[BevelReset(win)] dx := "dx=" || WAttrib(win, "dx") dy := "dy=" || WAttrib(win, "dy") every WAttrib(b.shadow | b.hilite, dx, dy) return b end # BevelCircle(win, x, y, r, bw) -- draw beveled circle procedure BevelCircle(win, x, y, r, bw) #: draw beveled circle local b, upper, lower, a static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return BevelCircle((\&window | runerr(140)), win, x, y, r) b := bev_lookup(win) /r := 6 /bw := 2 if bw >= 0 then { upper := b.hilite lower := b.shadow } else { upper := b.shadow lower := b.hilite bw := -bw } a := -&pi / 8 while (bw -:= 1) >= 0 do { DrawCircle(lower, x, y, r, a, &pi) DrawCircle(upper, x, y, r, a + &pi, &pi) r -:= 1 } return win end # BevelDiamond(win, x, y, r, bw) -- draw beveled diamond procedure BevelDiamond(win, x, y, r, bw) #: draw beveled diamond local b, upper, lower static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return BevelDiamond((\&window | runerr(140)), win, x, y, r) b := bev_lookup(win) /r := 6 /bw := 3 if bw >= 0 then { upper := b.hilite lower := b.shadow } else { upper := b.shadow lower := b.hilite bw := -bw } while (bw -:= 1) >= 0 do { DrawLine(lower, x - r, y, x, y + r, x + r, y) DrawLine(upper, x - r, y, x, y - r, x + r, y) r -:= 1 } return win end # BevelTriangle(win, x, y, r, o, bw) -- draw beveled triangle procedure BevelTriangle(win, x, y, r, o, bw) local b, upper, lower static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return BevelTriangle((\&window | runerr(140)), win, x, y, r, o) b := bev_lookup(win) /r := 6 /bw := 2 if bw >= 0 then { upper := b.hilite lower := b.shadow } else { upper := b.shadow lower := b.hilite bw := -bw } while (bw -:= 1) >= 0 do { case o of { default: { #"n" DrawLine(lower, x - r, y + r, x + r, y + r, x, y - r) DrawLine(upper, x - r, y + r, x, y - r) } "s": { DrawLine(lower, x, y + r, x + r, y - r) DrawLine(upper, x, y + r, x - r, y - r, x + r, y - r) } "e": { DrawLine(lower, x - r, y + r, x + r, y) DrawLine(upper, x - r, y + r, x - r, y - r, x + r, y) } "w": { DrawSegment(lower, x - r, y, x + r, y + r, x + r, y + r, x + r, y-r) DrawLine(upper, x - r, y, x + r, y - r) } } r -:= 1 } return win end # BevelSquare(win, x, y, r, bw) -- draw beveled square procedure BevelSquare(win, x, y, r, bw) #: draw beveled square static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return BevelSquare((\&window | runerr(140)), win, x, y, r) /r := 6 return BevelRectangle(win, x - r, y - r, 2 * r + 1, 2 * r + 1, bw) end # RidgeRectangle(win, x, y, w, h, bw) -- draw ridged rectangle procedure RidgeRectangle(win, x, y, w, h, bw) #: draw ridged rectangle static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return RidgeRectangle((\&window | runerr(140)), win, x, y, w, h) /bw := 2 return GrooveRectangle(win, x, y, w, h, -bw) end # GrooveRectangle(win, x, y, w, h, bw) -- draw grooved rectangle procedure GrooveRectangle(win, x, y, w, h, bw) #: draw grooved rectangle local abw static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return GrooveRectangle((\&window | runerr(140)), win, x, y, w, h) /x := -WAttrib(win, "dx") /y := -WAttrib(win, "dy") /w := WAttrib(win, "width") - (x + WAttrib(win, "dx")) /h := WAttrib(win, "height") - (y + WAttrib(win, "dy")) if w < 0 then x -:= (w := -w) if h < 0 then y -:= (h := -h) /bw := 2 if bw >= 0 then bw := (bw + 1) / 2 else bw := -((-bw + 1) / 2) abw := abs(bw) BevelRectangle(win, x, y, w, h, -bw) BevelRectangle(win, x + abw, y + abw, w - 2 * abw, h - 2 * abw, bw) return win end # BevelRectangle(win, x, y, w, h, bw) -- draw beveled rectangle # # bw is the border width (>0 for raised bevel, <0 for sunken bevel). # (x,y,w,h) bounds the entire beveled rectangle, not the usable area inside. procedure BevelRectangle(win, x, y, w, h, bw) #: draw beveled rectangle local b, upper, lower, xx, yy static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return BevelRectangle((\&window | runerr(140)), win, x, y, w, h) b := bev_lookup(win) /x := -WAttrib(win, "dx") /y := -WAttrib(win, "dy") /w := WAttrib(win, "width") - (x + WAttrib(win, "dx")) /h := WAttrib(win, "height") - (y + WAttrib(win, "dy")) if w < 0 then x -:= (w := -w) if h < 0 then y -:= (h := -h) /bw := 2 if bw >= 0 then { upper := b.hilite lower := b.shadow } else { upper := b.shadow lower := b.hilite bw := -bw } xx := x + w yy := y + h FillRectangle(lower, x, yy, w, -bw, xx, y, -bw, h) while (bw -:= 1) >= 0 do { DrawLine(upper, x, yy -:= 1, x, y, xx -:= 1, y) x +:= 1 y +:= 1 } return win end # DrawRidge(win, x1, y1, x2, y2, w) -- draw a ridged line # # If w is negative, a groove is drawn instead. procedure DrawRidge(win, x1, y1, x2, y2, w) #: draw ridged line static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return DrawRidge((\&window | runerr(140)), win, x1, y1, x2, y2) /w := 2 DrawGroove(win, x1, y1, x2, y2, -w) return win end # DrawGroove(win, x1, y1, x2, y2, w) -- draw a grooved line # # If w > 0, draw groove of width 2. # If w = 0, erase groove/ridge of width 2. # If w < 0, draw ridge of width 2. # # Horizontal and vertical grooves fill the same pixels as lines drawn # linewidth=2. Angled grooves are not necessarily the same, though. procedure DrawGroove(win, x1, y1, x2, y2, w) #: draw grooved line local a, n, b, upper, lower, fg static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return DrawGroove((\&window | runerr(140)), win, x1, y1, x2, y2) /w := 2 x1 := integer(x1) y1 := integer(y1) x2 := integer(x2) y2 := integer(y2) if w ~= 0 then { # if really drawing b := bev_lookup(win) upper := b.shadow lower := b.hilite } else { fg := Fg(win) # if erasing, draw in bg color Fg(win, Bg(win)) upper := lower := win } a := atan(y2 - y1, x2 - x1) if a < 0 then a +:= &pi n := integer(8 * a / &pi) if w < 0 then # if groove/ridge swap upper :=: lower if n = 2 then # if tricky illumination angle upper :=: lower if 2 <= n <= 5 then { # approximately vertical DrawLine(upper, x1 - 1, y1, x2 - 1, y2) DrawLine(lower, x1, y1, x2, y2) } else { # approximately horizontal DrawLine(upper, x1, y1 - 1, x2, y2 - 1) DrawLine(lower, x1, y1, x2, y2) } Fg(win, \fg) # restore foreground if changed return win end # FillSquare(win, x, y, r) -- fill square centered at (x,y) procedure FillSquare(win, x, y, r) #: draw filled square static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return FillSquare((\&window | runerr(140)), win, x, y) return FillRectangle(win, x - r, y - r, 2 * r + 1, 2 * r + 1) end # FillDiamond(win, x, y, r) -- fill diamond centered at (x,y) procedure FillDiamond(win, x, y, r) #: draw filled diamond static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return FillDiamond((\&window | runerr(140)), win, x, y) return FillPolygon(win, x - r, y, x, y + r + 1, x + r + 1, y, x, y - r - 1) end # FillTriangle(win, x, y, r, o) -- fill triangle centered at (x,y) # # r is "radius" (1/2 of side of enclosing square) # o is orientation ("n", "s", "e", "w") procedure FillTriangle(win, x, y, r, o) #: draw filled triangle static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then return FillTriangle((\&window | runerr(140)), win, x, y, r) return case o of { default: #"n" FillPolygon(win, x - r - 1, y + r + 1, x, y - r, x + r + 1, y + r + 1) "s": FillPolygon(win, x - r, y - r, x, y + r, x + r, y - r) "e": FillPolygon(win, x - r, y - r, x + r, y, x - r, y + r) "w": FillPolygon(win, x + r + 1, y - r - 1, x - r, y, x + r + 1, y + r + 1) } end