diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-01-27 23:51:56 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-01-27 23:51:56 +0000 |
commit | 6ab0c0f5bf14ed9c15370407b9ee7e0b4b089ae1 (patch) | |
tree | 926065cf45450116098db664e3c61dced9e1f21a /ipl/gprogs | |
download | icon-6ab0c0f5bf14ed9c15370407b9ee7e0b4b089ae1.tar.gz |
Initial upstream version 9.4.3upstream/9.4.3
Diffstat (limited to 'ipl/gprogs')
173 files changed, 30874 insertions, 0 deletions
diff --git a/ipl/gprogs/autotile.icn b/ipl/gprogs/autotile.icn new file mode 100644 index 0000000..631e9b6 --- /dev/null +++ b/ipl/gprogs/autotile.icn @@ -0,0 +1,87 @@ +############################################################################ +# +# File: autotile.icn +# +# Subject: Program to produce tile from XBM image +# +# Author: Ralph E. Griswold +# +# Date: January 3, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program creates a tile of a specified size by processing an +# XBM image file. The tile grid is "laid over" the image to form squares. +# +# The non-white pixels in each square of the image are counted. If the +# percentage of non-white pixels exceeds a specified threshold, the +# corresponding bit in the tile is set. +# +# The supported options are: +# +# -h i tile height, default 32 +# -w i tile width, default 32 +# -t r threshold, default 0.50 +# +############################################################################ +# +# Links: options, patutils +# +############################################################################ + +link options +link patutils + +global pixmap + +procedure main(args) + local x, y, pixels, i, j, size, rows, wcell, hcell + local opts, input, w, h, t, xoff, yoff + + opts := options(args, "t.h+w+") + + input := open(args[1]) | stop("*** cannot open input file") + + pixmap := [] # image array + + every put(pixmap, xbm2rows(input)) + + w := \opts["w"] | 32 + h := \opts["h"] | 32 + t := \opts["t"] | 0.50 + + wcell := *pixmap[1] / w + hcell := *pixmap / h + + size := real(wcell * hcell) + + rows := list(h, repl("0", w)) # tile + + x := 0 + + every i := 1 to w do { + y := 0 + every j := 1 to h do { + pixels := 0 + xoff := x + 1 + every 1 to wcell do { + yoff := y + 1 + every 1 to hcell do { + every pixels +:= pixmap[yoff, xoff] + yoff +:= 1 + } + xoff +:= 1 + } + if pixels / size > t then rows[j, i] := "1" + y +:= hcell + } + x +:= wcell + } + + write(rows2pat(rows)) + +end diff --git a/ipl/gprogs/binpack.icn b/ipl/gprogs/binpack.icn new file mode 100644 index 0000000..0bc066d --- /dev/null +++ b/ipl/gprogs/binpack.icn @@ -0,0 +1,627 @@ +############################################################################ +# +# File: binpack.icn +# +# Subject: Program to demonstrate some bin packing algorithms +# +# Author: Gregg M. Townsend +# +# Date: June 23, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: binpack [window options] +# +# Binpack illustrates several approximation algorithms for solving the +# one-dimensional bin packing problem. +# +# For references, see the "info" screen. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: numbers, graphics, random, vsetup +# +############################################################################ + +link numbers +link graphics +link random +link vsetup + +$define Version "Binpack, Version 1.0 (September, 1993)" + +$define MAXK 250 # max value of `k' allowed + +$define FULL 61261200 # value representing a full bin + # (least common multiple of {1 to 18, 20, and 25}) + +$define X0 120 # left edge of bin display +$define DY 165 # vertical spacing +$define YSCALE 155 # scaling for one display + +$define BX1 10 # x-coord for first button column +$define BX2 60 # x-coord for second button column +$define BWIDTH 40 # button width +$define BHEIGHT 16 # button height +$define BSPACE 16 # button spacing + + +# parameter values +global maxsize # maximum piece size +global nreload # number of pieces on a reload +global kvalue # constant `k' used in some algorithms + +# current source set +global pieces # list of piece sizes +global dx # distance between bins +global bwidth # bin width +global cdiv # divisor for converting size to color index + +# current output parameters +global bin # list of current bin sizes +global nfilled # number of bins (partially) filled +global xll, yll # lower left corner of display area + + +# miscellany +global width # window width +global color # array of GCs of different colors +global glossary # list of explanations + + + +# Future possibilities: +# +# better layout -- critical controls are too crowded +# add artificial delays for better visualization +# implement O(n log n) algs as such instead of O(n^2) +# n.b. this may not help because can't use Icon's native data structs + + + +######################### main program ######################### + +procedure main(args) + local v, r, c, gc + + randomize() # set irreproducible mode + + v := ui(args) # open window, set up vib-built vidgets + r := v["root"] + glossary := [] + addbutton(r, "BF", bestfit, "Best Fit", "picks the fullest possible bin") + addbutton(r, "WF", worstfit, "Worst Fit", "picks the emptiest bin") + addbutton(r, "AWF",nearworst,"Almost Worst Fit", "picks second-emptiest bin") + addbutton(r, "FF", firstfit, "First Fit", "picks the oldest possible bin") + addbutton(r, "LF", lastfit, "Last Fit", "picks the newest possible bin") + addbutton(r, "NF", nextfit, "Next Fit", "tries only the current bin") + addbutton(r, "N(k)", nextk, "Next-k Fit", "tries the k newest bins") + addbutton(r, "H(k)", harmonic, "Harmonic Algorithm", + "classifies into {1/1,1/2,...,1/k}") + addbutton(r, "G(k)", gxfit, "Group-X Fit", "groups into k equal classes") + VResize(r) + + # workaround freeing of gray highlight color seen with "binpack -Bwhite" + BevelReset() # work around color freeing bug + + color := [] + if WAttrib("depth") = 1 then + put(color, &window) + else { + # make a set of colors for different bin heights + # note that exactly half are reds/yellows and half are blues & darker + every c := Blend( + "black", 1, "deep purple-magenta", 10, "cyan-blue", + 1, "reddish-yellow", 11, "orange-red") do { + gc := Clone(&window) + Shade(gc, c) + put(color, gc) + } + color := copy(color) # ensure contiguous + } + + # keep the following initializations in sync with initial slider positionm + setmax(v["max"], 20) # set maximum bin value + setbins(v["bins"], -100) # set number of bins + setk(v["kval"], -10) # set constant `k' value + + reload() # initialize random bins + status("") # display bin count + + &error := 1 + WAttrib("resize=on") + &error := 0 + + r.V.event := 1 # disable screen erase on resize + GetEvents(r, leftover) # enter event loop +end + + +# addbutton -- add a button (and a D variant) on every shelf + +procedure addbutton(r, label, proc, name, defn) + local v, n, y + static yoff + initial yoff := 0 + + y := yoff +:= BSPACE + while (y +:= DY) < WAttrib("height") do { + Vbutton(r, BX1, y, r.win, label, pack, proc, V_RECT, BWIDTH, BHEIGHT) + Vbutton(r, BX2, y, r.win, label||"D", pack, proc, V_RECT, BWIDTH, BHEIGHT) + } + put(glossary, left(label, 6) || left(name, 20) || defn) + return +end + + + +######################### parameter setting ######################### + +# These routines are called during initialization and in response to +# slider movement. + + +# setk(v, n) -- set value of constant `k', based on 1 - 100 slider scale + +procedure setk(v, n) + if n >= 0 then # if slider call + n := integer(MAXK ^ ((n / 100.0) ^ 0.70)) # convert nonlinearly + else + n := -n # initial call + kvalue := roundoff(n) + GotoXY(v.ax, v.ay + v.ah + 14) + WWrites(left("k=" || kvalue, 8)) + return +end + + +# setmax(v, n) -- set maxsize, based on 1 - 20 slider scale. + +procedure setmax(v, n) + local fract + + fract := n / 20.0 + maxsize := integer(fract * FULL) + GotoXY(v.ax, v.ay + v.ah + 14) + WWrites(" max size ", ((fract || "00") ? move(4))) + return +end + + +# setbins(v, n) -- set number of bins, based on 1 - 100 slider scale + +procedure setbins(v, n) + local s, max + + max := WAttrib("width") - 40 - X0 # max that will fit on screen + if &shift then # allow more if shifted + max /:= 1.1 * (maxsize / (2.0 * FULL)) + + if n >= 0 then # if slider call + n := integer(max ^ ((n / 100.0) ^ 0.40)) # convert nonlinearly + else + n := -n # initial call + n <:= 5 + n := roundoff(n, 5) # convert to round number + + nreload := n + s := center(nreload, 5) + GotoXY(v.ax + (v.aw - TextWidth(s)) / 2, v.ay + v.ah + 17) + WWrites(s) + return +end + + +# roundoff(n) -- truncate n to a nice number divisible by m (at least) + +procedure roundoff(n, m) + local d + + if n > 1000 then { + if n > 10000 then + d := 1000 + else if n > 5000 then + d := 500 + else + d := 100 + } + else if n > 500 then + d := 50 + else if n > 100 then + d := 10 + else if n > 50 then + d := 5 + n -:= n % \d + n -:= n % \m + return n +end + + +######################### bin packing primitives ######################### + + +# empty(n) -- empty shelf n + +procedure empty(n) + bin := list(*pieces, 0) + nfilled := 0 + xll := X0 + yll := n * DY + EraseArea(xll, yll - DY + 1, , DY) + width := WAttrib("width") + return +end + + +# place(p, b) -- add a piece of size p to bin b + +procedure place(p, b) + local o, t, x, y0, y1 + static invfull + initial invfull := 1.0 / FULL + + o := bin[b] | fail + if (t := o + p) > FULL then + fail + bin[b] := t + nfilled <:= b + if (x := xll + (b - 1) * dx) < width then { + y0 := integer(yll - YSCALE * o * invfull) + y1 := integer(yll - YSCALE * t * invfull) + 1 + FillRectangle(color[p / cdiv + 1], x, y1, bwidth, 0 < (y0 - y1)) + } + return +end + + +# status(s) -- write string s and shelf population at end of output shelf + +procedure status(s) + local x + + x := xll + nfilled * dx + 4 + x >:= width - 40 + GotoXY(x, yll - 15) + WWrites(s) + GotoXY(x, yll) + WWrites(nfilled) + return +end + + + +######################### source set manipulation ######################### + + +# reload() -- reload first shelf with random-sized pieces. + +procedure reload() + local i, j, z, p + + pieces := list(nreload) + empty(1) + dx := (width - 40 - X0) / nreload + dx <:= 1 + dx >:= 20 + bwidth := 4 * dx / 5 + bwidth <:= 1 + cdiv := (maxsize + *color - 1) / *color + every place(pieces[i := 1 to *pieces] := ?maxsize, i) + status("new") + return +end + + +# mix() -- randomly reorder the first shelf. +# +# if shifted, place equally-spaced using golden ratio + +procedure mix() + local i, n, p + + if &shift then { + n := integer(*pieces / &phi + 1) + while gcd(*pieces, n) > 1 do + n -:= 1 + i := 0 + every p := !sort(pieces) do { + i := (i + n) % *pieces + pieces[i + 1] := p + } + } + else + every i := *pieces to 2 by -1 do + pieces[?i] :=: pieces[i] + + empty(1) + every place(pieces[i := 1 to *pieces], i) + status("mix") + return +end + + + +# order() -- sort the first shelf in descending order +# +# if shifted, sort ascending + +procedure order() + local i + + pieces := sort(pieces) + if not &shift then + every i := 1 to *pieces / 2 do # change from ascending to descending + pieces[i] :=: pieces[-i] + + empty(1) + every place(pieces[i := 1 to *pieces], i) + status("sort") + return +end + + + +######################### packing algorithms ######################### + + + +# pack(x, v) -- execute packing algorithm connected with button x + +procedure pack(x, v) + local l, n, s, i + + if x.ax = BX2 then { + l := sort(pieces) # if second-column button, sort first + every i := 1 to *l/2 do # change from ascending to descending + l[i] :=: l[-i] + } + else + l := copy(pieces) + + n := x.ay / DY + 1 # compute shelf number + empty(n) # clear the shelf + + s := x.id(l) # call packing algorithm + status(\s | x.s) # display status + return +end + + +# nextfit(l) -- pack using next-fit algorithm + +procedure nextfit(l) + local p + + every p := !l do + place(p, nfilled | nfilled + 1) + return +end + + +# nextk(l) -- pack using next-k-fit algorithm + +procedure nextk(l) + local p + + every p := !l do + if nfilled <= kvalue then + place(p, 1 to nfilled + 1) + else + place(p, nfilled - kvalue + 1 to nfilled + 1) + return "N" || kvalue +end + + +# firstfit(l) -- pack using first-fit algorithm + +procedure firstfit(l) + local p + + every p := !l do + place(p, 1 to nfilled + 1) + return +end + + +# lastfit(l) -- pack using last-fit algorithm + +procedure lastfit(l) + local p + + every p := !l do + place(p, (nfilled to 1 by -1) | (nfilled + 1)) + return +end + + +# bestfit(l) -- pack using best-fit algorithm + +procedure bestfit(l) + local p, b, i, max, found + + every p := !l do { + max := FULL - p # fullest acceptable bin size + found := 0 # size of best bin found so far + b := nfilled + 1 # index of where found + every i := 1 to nfilled do + if found <:= (max >= bin[i]) then + b := i + place(p, b) # place in best bin found + } + return +end + + +# worstfit(l, n) -- pack using worst-fit algorithm + +procedure worstfit(l, n) + local p, b, i, found + + every p := !l do { + found := FULL - p # size of best bin found so far + b := nfilled + 1 # index of where found + every i := 1 to nfilled do + if found >:= bin[i] then + b := i + place(p, b) # place in best bin found + } + return +end + + +# nearworst(l, n) -- pack using almost-worst-fit algorithm + +procedure nearworst(l, n) + local p, a, b, i, found + + every p := !l do { + found := FULL - p # size of best bin found so far + a := b := &null + every i := 1 to nfilled do + if found >:= bin[i] then { + a := b + b := i + } + place(p, \a | \b | (nfilled + 1)) # place in second-best bin found + } + return +end + + +# harmonic(l, n) -- pack using (unmodified) harmonic algorithm + +procedure harmonic(l, n) + local curr, maxv, i, p, b + + curr := list(kvalue) # current bin for each class + maxv := list(kvalue) # maximum for each class + every i := 1 to kvalue do + maxv[i] := FULL / (kvalue - i + 1) + + every p := !l do { + p <= maxv[i := 1 to kvalue] # find class index i + b := curr[i] + if /b | (bin[b] + p > FULL) then + place(p, curr[i] := nfilled + 1) + else + place(p, b) + } + return "H" || kvalue +end + + +# gxfit(l, n) -- pack using group-x(k)-fit algorithm + +procedure gxfit(l, n) + local stk, maxv, i, s, p, b, d + + stk := [] # stacks of bins, one for each group + maxv := [] # maximum for each group + + # make k equally sized groups + d := FULL / kvalue + every i := 1 to kvalue do { + put(stk, []) + put(maxv, i * d - 1) + } + + every p := !l do { + # find group index i for piece + (p <= maxv[i := (1 to kvalue) | 0]) & (*stk[i] > 0) + b := pop(stk[i]) | (nfilled + 1) + place(p, b) + # now put bin back on a stack, if not too full + if (FULL - bin[b]) >= maxv[i := (kvalue - 1 to 1 by -1)] then + push(stk[i], b) + } + return "G" || kvalue +end + + + +######################### event miscellany ######################### + + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:lucidasanstypewriter-bold-12::0,0,860,675:Bin Packing",], + ["bins:Slider:h::10,48,100,15:0,100,40",setbins], + ["infob:Button:regular::10,111,40,17:info",info], + ["kval:Slider:h::10,135,100,15:0,100,30",setk], + ["max:Slider:h::10,10,100,15:1,20,20",setmax], + ["mix:Button:regular::10,68,30,17:mix",mix], + ["new:Button:regular::80,68,30,17:new",reload], + ["quit:Button:regular::70,110,40,17:quit",quit], + ["sort:Button:regular::10,87,35,17:sort",order], + ) +end +#===<<vib:end>>=== end of section maintained by vib + + + +# leftover() -- handle events that fall outside the vidgets +# +# Exits when certain keys are pressed and ignores other events. + +procedure leftover(e) + case e of { + QuitEvents(): exit() + &meta & !"nN": reload() + &meta & !"mM": mix() + &meta & !"sS": order() + &meta & !"iI": info() + } + return +end + + +# quit() -- handle "quit" button press + +procedure quit(x, v) + exit() +end + + +# info() -- handle "info" button press + +procedure info(x, v) + static text + initial { + text := ["", + Version, + "by Gregg Townsend, The University of Arizona", + "", + "", + "Glossary:", + ""] + every put(text, " " || !glossary) + put(text, + "", + "A `D' suffix indicates a variation where the input is sorted", + "in descending order before applying the algorithm.", + "", + "", + "For more information about bin packing algorithms, see:", + "", + " `Approximation Algorithms for Bin-Packing -- An Updated Survey'", + " by E.G. Coffman, Jr., M.R. Garey, and D.S. Johnson, in", + " Algorithm Design for Computer System Design, ed. by", + " Ausiello, Lucertini, and Serafini, Springer-Verlag, 1984", + "", + " `Fast Algorithms for Bin Packing' by David S. Johnson,", + " Journal of Computer and System Sciences 8, 272-314 (1974)", + "") + } + Notice ! text + return +end diff --git a/ipl/gprogs/bitdemo.icn b/ipl/gprogs/bitdemo.icn new file mode 100644 index 0000000..d66802b --- /dev/null +++ b/ipl/gprogs/bitdemo.icn @@ -0,0 +1,210 @@ +############################################################################ +# +# File: bitdemo.icn +# +# Subject: Program to demonstrate bitplanes +# +# Author: Gregg M. Townsend +# +# Date: November 14, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# bitdemo illustrates some of the techniques made available by the +# bitplane package in the program library. +# +# The upper rectangle is drawn using three bitplanes, reserving +# one plane for each of the primary colors. After clicking one of +# the "draw" or "erase" buttons, you can draw or erase any one of +# the bitplanes independently of the others. Notice what happens +# when the colors overlap. +# +# Drawing is not constrained to the rectangle so that you can see +# some of the possible consequences of using the bitplane routines +# improperly. +# +# The lower rectangle is drawn using four other bitplanes, one each +# for the four types of objects. Click once on a button to bring the +# objects of that type to the front. Click a second time to make them +# invisible. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, evmux, bitplane, graphics +# +############################################################################ + +link button +link evmux +link bitplane +link graphics + +$define BevelWidth 2 +$define WindowMargin 10 + + +global bitwin, rgbbase, panebase, panecolor + + +procedure main(args) + local win, m, b, w, h, i + local px, py, pw, ph, x, y, d, a + local bw, bh + local colors + + # get options and open window + win := Window("size=800,600", "font=Helvetica,bold,14", args) + + # ensure that we can get the color map entries we will need + bitwin := Clone(win) + panebase := AlcPlane(bitwin, 4) | stop("can't get 4 planes") + rgbbase := AlcPlane(bitwin, 3) | stop("can't get 3 planes") + + # get window geometry + m := WindowMargin # margins + b := BevelWidth + w := WAttrib("width") - 2 * m # usable width + h := WAttrib("height") - 2 * m # usable height + bw := 80 # button width + bh := 24 # button height + + # establish global sensors; override later with buttons etc. in some areas + sensor(win, &lpress, drag) + sensor(win, &ldrag, drag) + quitsensor(win) + button(win, "quit", argless, exit, m, m + h - bh, bw, bh) + + # build drawing window and initialize with overlapping circles + BevelRectangle(win, m + 100, m, w - 100, 250, -b) + colors := [ + Bg(win), "red", "yellow", "red-yellow", + "blue", "purple-magenta", "dark green", "dark brown"] + every i := 0 to 7 do + Color(bitwin, rgbbase + i, colors[i + 1]) + PlaneOp(bitwin, rgbbase, "copy") + FillRectangle(bitwin, m + 100 + b, m + b, w - 100 - 2 * b, 250 - 2 * b) + PlaneOp(bitwin, rgbbase+4, "set"); FillArc(bitwin, w/2-25, 100, 100, 100) + PlaneOp(bitwin, rgbbase+2, "set"); FillArc(bitwin, w/2, 50, 100, 100) + PlaneOp(bitwin, rgbbase+1, "set"); FillArc(bitwin, w/2+25, 100, 100, 100) + Deplane(bitwin) + + # set up related buttons + buttonrow(win, m, m, bw, bh, 0, bh + m, + "draw red", draw, 1, + "draw yel", draw, 2, + "draw blu", draw, 4, + &null, &null, &null, + "erase red", erase, 1, + "erase yel", erase, 2, + "erase blu", erase, 4, + ) + + # set up structure for pane demo + panecolor := table() + panecolor[0] := Bg(win) + px := m + 100 + py := m + 250 + 2 * m + pw := m + w - px + ph := m + h - py + Fg(bitwin, panebase) + FillRectangle(bitwin, px, py, pw, ph) + BevelRectangle(win, px, py, pw, ph, -b) + Clip(bitwin, px + b, py + b, pw - 2 * b, ph - 2 * b) + buttonrow(win, m, py, bw, bh, 0, bh + m, + "visible:", &null, &null, + "grid", mvplane, 1, + "curves", mvplane, 8, + "squares", mvplane, 2, + "circles", mvplane, 4, + ) + + # draw grid on plane 1 + FrontPlane(bitwin, panebase + 1, panecolor[1] := "light gray") + PlaneOp(bitwin, panebase + 1, "set") + every x := 20 to pw - 1 by 40 do + FillRectangle(bitwin, px + x, py + b, 3, ph - 2 * b) + every y := 20 to ph - 1 by 40 do + FillRectangle(bitwin, px + b, py + y, pw - 2 * b, 3) + + # draw curves on plane 8 + FrontPlane(bitwin, panebase + 8, panecolor[8] := "dark blue") + PlaneOp(bitwin, panebase + 8, "set") + every y := 20 to ph-40 by 30 do { + a := [bitwin] + every put(a, px + (0 to pw+24 by 25)) do + put(a, py + y + ?20) + every 1 to 3 do { + DrawCurve ! a + every a[3 to *a by 2] +:= 1 + } + } + + # draw squares on plane 2 + FrontPlane(bitwin, panebase + 2, panecolor[2] := "dark brown") + PlaneOp(bitwin, panebase + 2, "set") + d := 20 + every 1 to 50 do + FillRectangle(bitwin, px + ?(pw - d), py + ?(ph - d), d, d) + + # draw circles on plane 4 + FrontPlane(bitwin, panebase + 4, panecolor[4] := "dark moderate green") + PlaneOp(bitwin, panebase + 4, "set") + every 1 to 50 do { + d := 20 + ?10 + FillArc(bitwin, px + ?(pw - d), py + ?(ph - d), d, d) + } + + # enter event loop + Clip(bitwin) + evmux(win) +end + + +## draw(w, v) -- set plane and drawing op in response to "draw" button + +procedure draw(w, v) + PlaneOp(bitwin, rgbbase + v, "set") +end + + +## erase(w, v) -- set plane and drawing op in response to "erase" button + +procedure erase(w, v) + PlaneOp(bitwin, rgbbase + v, "clear") +end + + +## drag(w, dummy, x, y) -- handle mouse drag by drawing (or erasing) on window + +procedure drag(w, dummy, x, y) + FillRectangle(bitwin, x - 5, y - 5, 10, 10) +end + + +## mvplane(w, v, x, y) -- handle click on visibility buttons +# +# first click moves to front +# second click makes invisible + +procedure mvplane(w, v, x, y) + static prev, rep + initial prev := rep := 0 + + if prev ~=:= v then + rep := 0 # this is a new button + else + rep := (rep + 1) % 2 # repeat count for old button + + case rep of { + 0: FrontPlane(bitwin, panebase + v, panecolor[v]) + 1: BackPlane(bitwin, panebase + v, panecolor[0]) + } +end diff --git a/ipl/gprogs/blp2grid.icn b/ipl/gprogs/blp2grid.icn new file mode 100644 index 0000000..7114168 --- /dev/null +++ b/ipl/gprogs/blp2grid.icn @@ -0,0 +1,81 @@ +############################################################################ +# +# File: blp2grid.icn +# +# Subject: Program to convert BLP drawdown to grid image +# +# Author: Ralph E. Griswold +# +# Date: June 26, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# The following options are supported: +# +# -s i size of cells; default 5 +# -c s color for filling cells; default black +# +# Also handles row files. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, cells, convert, options, patutils, wopen +# +############################################################################ + +link basename +link cells +link convert +link options +link patutils +link wopen + +procedure main(args) + local rows, panel, input, line, name, opts, size, file, color + + opts := options(args, "s+c:") + + size := \opts["s"] | 5 + color := \opts["c"] | "black" + + while file := get(args) do { + input := open(file) | stop("*** cannot open pattern file") + rows := [] + line := read(input) | stop("empty file") + if upto("#", line) then rows := pat2rows(line) + else { + rows := [line] + while put(rows, read(input)) # read in row pattern + } + panel := matrixpanel(rows, size) + fill_cells(panel, rows, color) + name := basename(file, ".blp") + name := basename(name, ".rows") + WriteImage(panel.window, name || "_grid.gif") + WClose(panel.window) + close(input) + } + +end + +procedure fill_cells(panel, rows, cellcolor) + local i, j, color + + every i := 1 to *rows do { + every j := 1 to *rows[1] do { + color := if rows[i, j] == "1" then cellcolor else "white" + colorcell(panel, j, i, color) + } + } + + return + +end diff --git a/ipl/gprogs/blp2rows.icn b/ipl/gprogs/blp2rows.icn new file mode 100644 index 0000000..37a8825 --- /dev/null +++ b/ipl/gprogs/blp2rows.icn @@ -0,0 +1,38 @@ +############################################################################ +# +# File: blp2rows.icn +# +# Subject: Program to convert bi-level pattern to row file +# +# Author: Ralph E. Griswold +# +# Date: October 30, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: patutils +# +############################################################################ + +link patutils + +procedure main() + local rows + + rows := pat2rows(read()) + + every write(!rows) + +end diff --git a/ipl/gprogs/bme.icn b/ipl/gprogs/bme.icn new file mode 100644 index 0000000..2131a00 --- /dev/null +++ b/ipl/gprogs/bme.icn @@ -0,0 +1,176 @@ +############################################################################ +# +# File: bme.icn +# +# Subject: Program to edit bitmap +# +# Author: Clinton L. Jeffery +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 2.0 +# +############################################################################ +# +# A bitmap editor. This is really the PixMap editor +# pme.icn with colors set to black and white, and color changes disabled. +# +# Left and right mouse buttons draw black and white. +# Press q or ESC to quit; press s to save. Capital "S" prompts for +# and saves under a new filename. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen, xcompat +# +############################################################################ + +link wopen +link xcompat + +global w, WIDTH, HEIGHT, XBM, LMARGIN +global colors, colorbinds + +procedure main(argv) + local i, f, s, xpos, ypos, i8, j, j8, j8Plus, e, x, y + colors := [ "black", "white", "white" ] + i := 1 + XBM := ".xbm" + WIDTH := 32 + HEIGHT := 32 + if *argv>0 & argv[1][1:5]=="-geo" then { + i +:= 1 + if *argv>1 then argv[2] ? { + WIDTH := integer(tab(many(&digits))) | stop("geo syntax") + ="x" | stop("geo syntax") + HEIGHT := integer(tab(0)) | stop("geo syntax") + i +:= 1 + } + } + LMARGIN := WIDTH + if LMARGIN < 65 then LMARGIN := 65 + if (*argv >= i) & (f := open(s := (argv[i] | (argv[i]||XBM)))) then { + close(f) + w:= WOpen("label=BitMap", "image="||s, "cursor=off") | + stop("cannot open window") + WIDTH <:= WAttrib(w, "width") + HEIGHT <:= WAttrib(w, "height") + pos := WAttrib(w, "pos") + pos ? { + xpos := tab(many(&digits)) | stop(image(pos)) + ="," + ypos := tab(0) + } + WAttrib(w, "posx="||xpos, "posy="||ypos, + "width="||(WIDTH*8+LMARGIN+5), "height="||(HEIGHT*8)) + Event(w) + every i := 0 to HEIGHT-1 do { + i8 := i*8 + every j := 0 to WIDTH-1 do { + j8 := j*8 + j8Plus := j8 + LMARGIN + 5 + CopyArea(w, w, j, i, 1, 1, j8Plus, i8) + CopyArea(w, w, j, i, 1, 1, j8Plus+1, i8) + CopyArea(w, w, j8Plus, i8, 2, 1, j8Plus+2,i8) + CopyArea(w, w, j8Plus, i8, 4, 1, j8Plus+4, i8) + CopyArea(w, w, j8Plus, i8, 8, 1, j8Plus, i8+1) + CopyArea(w, w, j8Plus, i8, 8, 2, j8Plus, i8+2) + CopyArea(w, w, j8Plus, i8, 8, 4, j8Plus, i8+4) + } + } + } + else { + w:= WOpen("label=BitMap", "cursor=off", "width="||(LMARGIN+WIDTH*8+5), + "height="||(HEIGHT*8+5)) | + stop("cannot open window") + } + + colorbinds := [ XBind(w,"fg="||colors[1]), + XBind(w,"fg="||colors[2]), + XBind(w,"fg="||colors[3]) ] + every i := 1 to 3 do { + XDrawArc(w, 4+i*10, HEIGHT+68, 7, 22) + XFillArc(colorbinds[i], 5+i*10, HEIGHT+70, 5, 20) + } + DrawRectangle(w, 5, HEIGHT+55, 45, 60) + DrawRectangle(w, 25, HEIGHT+50, 5, 5) + DrawCurve(w, 27, HEIGHT+50, + 27, HEIGHT+47, + 15, HEIGHT+39, + 40, HEIGHT+20, + 25, HEIGHT+5) + + Fg(w, "black") + every i := 0 to HEIGHT-1 do + every j := 0 to WIDTH-1 do + DrawRectangle(w, j*8+LMARGIN+5, i*8, 8, 8) + + DrawLine(w, 0, HEIGHT, WIDTH, HEIGHT, WIDTH, 0) + + repeat { + case e := Event(w) of { + "q"|"\e": return + "s"|"S": { + if /s | (e=="S") then s := getfilename() + write("saving image ", s, " with width ", image(WIDTH), + " height ", image(HEIGHT)) + WriteImage(w, s, 0, 0, WIDTH, HEIGHT) + } + &lpress | &ldrag | &mpress | &mdrag | &rpress | &rdrag : { + + x := (&x - LMARGIN - 5) / 8 + y := &y / 8 + + if (y < 0) | (y > HEIGHT-1) | (x > WIDTH) then next + if (x < 0) then { +# if &x < 21 then getacolor(1, "left") +# else if &x < 31 then getacolor(2, "middle") +# else getacolor(3, "right") +# until Event(w) === (&mrelease | &lrelease | &rrelease) + } + else dot(x, y, (-e-1)%3) + } + } + } +end + +#procedure getacolor(n, s) +# wtmp := WOpen("label=" || labelimage(s||" button: "), "lines=1") | +# stop("can't open temp window") +# writes(wtmp,"[",colors[n],"] ") +# theColor := read(wtmp) | stop("read fails") +# close(wtmp) +# wtmp := colorbinds[n] | stop("colorbinds[n] fails") +# Fg(wtmp, theColor) | write("XFG(", theColor, ") fails") +# XFillArc(wtmp, 5+n*10, HEIGHT+70, 5, 20) +# colors[n] := theColor +#end + +procedure dot(x, y, color) + if (x|y) < 0 then fail + FillRectangle(colorbinds[color+1], x*8+LMARGIN+5, y*8, 8, 8) + DrawPoint(colorbinds[color+1], x, y) + DrawRectangle(w, x*8+LMARGIN+5, y*8, 8, 8) +end + +procedure getfilename() + local s, pos, wprompt, rv + pos := "pos=" + every s := QueryPointer() do pos||:= (s-10)||"," + wprompt := WOpen("lable=Enter a filename to save the pixmap", + "font=12x24", "lines=1", pos[1:-1]) | stop("can't xprompt") + rv := read(wprompt) + close(wprompt) + if not find(XBM, rv) then rv ||:= XBM + return rv +end diff --git a/ipl/gprogs/bpack.icn b/ipl/gprogs/bpack.icn new file mode 100644 index 0000000..1a01764 --- /dev/null +++ b/ipl/gprogs/bpack.icn @@ -0,0 +1,435 @@ +############################################################################ +# +# File: bpack.icn +# +# Subject: Program to demonstrate some bin packing algorithms +# +# Author: Gregg M. Townsend +# +# Date: August 7, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: bpack [window options] +# +# Bpack illustrates several approximation algorithms for solving the +# one-dimensional bin packing problem. +# +# For a discussion of this program, see +# http://www.cs.arizona.edu/icon/oddsends/bpack/bpack.htm +# +# For references, see the "about" screen. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: numbers, graphics, random, vsetup +# +############################################################################ + + + +link numbers +link graphics +link random +link vsetup + +$define Version "Binpack Lite (November, 1997)" + +$define FULL 61261200 # value representing a full bin + # (least common multiple of {1 to 18, 20, and 25}) + +$define PieceWidth 6 # width of one piece +$define BinWidth 7 # width of one bin + + + +# pieces +global pieces # list of piece sizes + +# current output parameters +global xll, yll # display origin +global bin # list of current bin sizes +global nfilled # number of bins (partially) filled + +# colors +global color # array of GCs of different colors +global cscale # conversion from piece size to color index + +# display regions +global shelf1 # input segments +global shelf2 # packed bins + + + +######################### main program ######################### + +procedure main(args) + local v, r, c, gc + + randomize() # set irreproducible mode + + v := ui(args) # open window, set up vib-built vidgets + r := v["root"] + shelf1 := v["shelf1"] + shelf2 := v["shelf2"] + if shelf1.uw ~= shelf2.uw | shelf1.uw ~= shelf2.uw then + runerr(500, "inconsistent layout") + + # make a set of colors for different bin heights + # note that exactly half are reds/yellows and half are blues & darker + color := [] + every c := Blend( + "black", 1, "deep purple-magenta", 10, "cyan-blue", + 1, "reddish-yellow", 11, "orange-red") do { + gc := Clone(&window) + Fg(gc, c) + put(color, gc) + } + color := copy(color) # ensure contiguous array + cscale := *color / real(FULL + 1) + + reload() # initialize random bins + GetEvents(r) # enter event loop +end + + + +######################### bin packing primitives ######################### + + +# prepare(v) -- prepare shelf v for placing pieces + +procedure prepare(v) + xll := v.ux + yll := v.uy + v.uh + bin := list(*pieces, 0) + nfilled := 0 + EraseArea(v.ux, v.uy, v.uw, v.uh) + return +end + + +# place(p, b) -- add a piece of size p to bin b + +procedure place(p, b) + local o, t, x, y0, y1 + static m + initial m := shelf1.uh / real(FULL) + + o := bin[b] | fail + if (t := o + p) > FULL then + fail + bin[b] := t + nfilled <:= b + + x := xll + (b - 1) * (PieceWidth + 1) + y0 := integer(yll - m * o) + y1 := integer(yll - m * t) + 1 + FillRectangle(color[cscale * p + 1], x, y1, PieceWidth, 0 < (y0 - y1)) + return +end + + +# status(s) -- write string s and shelf population at end of output shelf + +procedure status(s) + local x + + x := xll + nfilled * BinWidth + 4 + x >:= xll + shelf1.uw - TextWidth("000 ") + GotoXY(x, yll - WAttrib("leading") - WAttrib("descent")) + WWrites(s) + GotoXY(x, yll - WAttrib("descent")) + WWrites(nfilled) + return +end + + + +######################### source set manipulation ######################### + + +# reload() -- reload first shelf with random-sized pieces. + +procedure reload() + local i, j, z, p + + pieces := list(shelf1.uw / BinWidth) + prepare(shelf1) + every place(pieces[i := 1 to *pieces] := ?FULL, i) + status("") + return +end + + + +# mix() -- randomly reorder the first shelf. + +procedure mix() + local i + + every i := *pieces to 2 by -1 do + pieces[?i] :=: pieces[i] + + prepare(shelf1) + every place(pieces[i := 1 to *pieces], i) + status("") + return +end + + + +# regular() -- place equally-spaced using golden ratio + +procedure regular() + local i, n, p + + n := integer(*pieces / &phi + 1) + while gcd(*pieces, n) > 1 do + n -:= 1 + i := 0 + every p := !sort(pieces) do { + i := (i + n) % *pieces + pieces[i + 1] := p + } + + prepare(shelf1) + every place(pieces[i := 1 to *pieces], i) + status("") + return +end + + + +# ascending() -- sort the first shelf in ascending order + +procedure ascending() + local i + + pieces := sort(pieces) + prepare(shelf1) + every place(pieces[i := 1 to *pieces], i) + status("") + return +end + + + +# descending() -- sort the first shelf in descending order + +procedure descending() + local i + + pieces := sort(pieces) + every i := 1 to *pieces / 2 do # change from ascending to descending + pieces[i] :=: pieces[-i] + + prepare(shelf1) + every place(pieces[i := 1 to *pieces], i) + status("") + return +end + + + +######################### packing algorithms ######################### + + +# nextfit(l) -- pack using next-fit algorithm + +procedure nextfit(l) + local p + + every p := !l do + place(p, nfilled | nfilled + 1) + return +end + + + +# firstfit(l) -- pack using first-fit algorithm + +procedure firstfit(l) + local p + + every p := !l do + place(p, 1 to nfilled + 1) + return +end + + +# lastfit(l) -- pack using last-fit algorithm + +procedure lastfit(l) + local p + + every p := !l do + place(p, (nfilled to 1 by -1) | (nfilled + 1)) + return +end + + +# bestfit(l) -- pack using best-fit algorithm + +procedure bestfit(l) + local p, b, i, max, found + + every p := !l do { + max := FULL - p # fullest acceptable bin size + found := 0 # size of best bin found so far + b := nfilled + 1 # index of where found + every i := 1 to nfilled do + if found <:= (max >= bin[i]) then + b := i + place(p, b) # place in best bin found + } + return +end + + +# worstfit(l, n) -- pack using worst-fit algorithm + +procedure worstfit(l, n) + local p, b, i, found + + every p := !l do { + found := FULL - p # size of best bin found so far + b := nfilled + 1 # index of where found + every i := 1 to nfilled do + if found >:= bin[i] then + b := i + place(p, b) # place in best bin found + } + return +end + + +# nearworst(l, n) -- pack using almost-worst-fit algorithm + +procedure nearworst(l, n) + local p, a, b, i, found + + every p := !l do { + found := FULL - p # size of best bin found so far + a := b := &null + every i := 1 to nfilled do + if found >:= bin[i] then { + a := b + b := i + } + place(p, \a | \b | (nfilled + 1)) # place in second-best bin found + } + return +end + + + +######################### event handling ######################### + + + +# menu_cb(v, a) -- File and Reorder menu callback + +procedure menu_cb(v, a) + case a[1] of { + "About": about() + "New": reload() + "Quit": exit() + "Random": mix() + "Regular": regular() + "Ascending": ascending() + "Descending": descending() + } +end + + + +# pack_cb(v, a) -- Pack menu callback + +procedure pack_cb(v, a) + local s, p + + a[1] ? { + s := tab(upto(' ')) # get 2- or 3-letter name + } + + prepare(shelf2) # clear the shelf + p := copy(pieces) + case s of { + "FF": firstfit(p) + "LF": lastfit(p) + "NF": nextfit(p) + "BF": bestfit(p) + "WF": worstfit(p) + "AWF": nearworst(p) + } + + status(s) + return +end + + + +# about() -- handle "about" menu entry + +procedure about(x, v) + static text + initial text := ["", + Version, + "by Gregg Townsend, The University of Arizona", + "", + "", + "BF Best Fit picks the fullest possible bin", + "WF Worst Fit picks the emptiest bin", + "AWF Almost Worst Fit picks second-emptiest bin", + "FF First Fit picks the oldest possible bin", + "LF Last Fit picks the newest possible bin", + "NF Next Fit tries only the current bin", + "", + "", + "For more information about bin packing algorithms, see:", + "", + " `Approximation Algorithms for Bin-Packing -- An Updated Survey'", + " by E.G. Coffman, Jr., M.R. Garey, and D.S. Johnson, in", + " Algorithm Design for Computer System Design, ed. by", + " Ausiello, Lucertini, and Serafini, Springer-Verlag, 1984", + "", + " `Fast Algorithms for Bin Packing' by David S. Johnson,", + " Journal of Computer and System Sciences 8, 272-314 (1974)", + ""] + + Notice ! text + return +end + + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=600,400", "bg=pale gray", "label=Bin Packer"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,600,400:Bin Packer",], + ["file:Menu:pull::0,0,36,21:File",menu_cb, + ["About","New","Quit"]], + ["line:Line:::0,22,599,22:",], + ["pack:Menu:pull::93,0,36,21:Pack",pack_cb, + ["FF first fit","LF last fit","NF next fit","BF best fit","WF worst fit", + "AWF almost worst"]], + ["reorder:Menu:pull::36,0,57,21:Reorder",menu_cb, + ["Random","Regular","Ascending","Descending"]], + ["shelf1:Rect:sunken::12,34,576,170:",], + ["shelf2:Rect:sunken::12,217,576,170:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/breakout.icn b/ipl/gprogs/breakout.icn new file mode 100644 index 0000000..28559f1 --- /dev/null +++ b/ipl/gprogs/breakout.icn @@ -0,0 +1,720 @@ +############################################################################ +# +# File: breakout.icn +# +# Subject: Program for Breakout game +# +# Author: Nathan J. Ranks +# +# Date: September 3, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Breakout game +# +# Infinite balls, Left or Right click to start or restart after losing ball +# 9 levels - can select any level when not active using 1-9 +# 1 hit, 2 hit, 3 hit, and invincible blocks can be used for levels +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics +# +############################################################################ + +link graphics +global sphere, blank #sphere and blank sphere +global X, Y #coordinates of sphere +global block_positions #string of whether or not position has block +global path, angle #direction of sphere travel +global wait #pause interval used with delay() +global level #current level +global hit #sphere and block contact flag +global blockclr1, blockclr2, blockclr3, invincclr + +procedure main() + local e + blockclr1 := "dark blue" #default 1 hit block color + blockclr2 := "dark red" #default 2 hit block color + blockclr3 := "dark green" #default 3 hit block color + invincclr := "black" #default invincible block color + + WOpen("size=293,320") | stop("can't open window") + + sphere := "3,g16,~0~_ + 000_ + ~0~" #black sphere + + blank := "3,g16,~F~_ + FFF_ + ~F~" #white sphere to erase + + level := 1 #default start level + create_blocks() #as the name suggests + + Fg("black") #default pad color + DrawLine(124,310,158,310) #default pad position + DrawImage(140, 304, sphere) #default sphere position + X := 140 #default x position + Y := 304 #default y position + path := "up_left" #default sphere direction + angle := 60 #default sphere angle + hit := 0 + +repeat { + if e := Event() then { + if ( e === &lpress ) then { + Fg("black") + DrawLine(124,310,158,310) #reset default + DrawImage(140, 304, sphere) #reset default + Y := 304 #reset default + path := "up_left" #reset default + angle := 60 #reset default + hit := 0 + + X := &x + DrawImage(140, 304, blank) + move_pad() + move_sphere() + } + if ( e === &rpress ) then { + Fg("black") + DrawLine(124,310,158,310) #reset default + DrawImage(140, 304, sphere) #reset default + Y := 304 #reset default + path := "up_right" #reset default + angle := 60 #reset default + hit := 0 + + X := &x + DrawImage(140, 304, blank) + move_pad() + move_sphere() + } + if ( e === "1" ) then { #change to level 1 + level := 1 + create_blocks() + } + if ( e === "2" ) then { #change to level 2 + level := 2 + create_blocks() + } + if ( e === "3" ) then { #change to level 3 + level := 3 + create_blocks() + } + if ( e === "4" ) then { #change to level 4 + level := 4 + create_blocks() + } + if ( e === "5" ) then { #change to level 5 + level := 5 + create_blocks() + } + if ( e === "6" ) then { #change to level 6 + level := 6 + create_blocks() + } + if ( e === "7" ) then { #change to level 7 + level := 7 + create_blocks() + } + if ( e === "8" ) then { #change to level 8 + level := 8 + create_blocks() + } + if ( e === "9" ) then { #change to level 9 + level := 9 + create_blocks() + } + } +} +end + + + +#this keeps track of where the pad should be according +#to where the mouse pointer is + +procedure move_pad() + &x := image(WAttrib("pointerx")) #get pointer position + &y := image(WAttrib("pointery")) #get pointer position + EraseArea(0,310,293,310) #erease old pad + Fg("black") #make sure color is correct + DrawLine(&x-12,310,&x+12,310) #draw new pad +return +end + + + +#this keeps track of sphere location and movement within the window. +#hits on walls will change direction +#hit on pad will change direction and possibly angle + +procedure move_sphere() +wait := 9 +while ( Y < 312 ) do { + if ( path == "up_right" ) then { + delay(wait) + move_pad() + GO_UP_RIGHT() + hit := 0 + if ( X > 285 ) then { + path := "up_left" + } + if ( Y < 0 ) then { + path := "down_right" + } + } + if ( path == "up_left" ) then { + delay(wait) + move_pad() + GO_UP_LEFT() + hit := 0 + if ( X < 0 ) then { + path := "up_right" + } + if ( Y < 0 ) then { + path := "down_left" + } + } + if ( path == "down_right" ) then { + delay(wait) + move_pad() + GO_DOWN_RIGHT() + hit := 0 + if ( X > 285 ) then { + path := "down_left" + } + if ( (Y = 303) | (Y = 304) | (Y = 305) ) then { + if ( ((X+1) < &x+13) & ((X+1) > &x-13) ) then { + path := "up_right" + if ( (X+1) > &x-13 ) then { + angle := 30 + } + if ( (X+1) > &x-6 ) then { + angle := 60 + } + if ( (X+1) > &x+6 ) then { + angle := 30 + } + } + } + } + if ( path == "down_left" ) then { + delay(wait) + move_pad() + GO_DOWN_LEFT() + hit := 0 + if ( X < 0 ) then { + path := "down_right" + } + if ( (Y = 303) | (Y = 304) | (Y = 305) ) then { + if ( ((X+1) < &x+13) & ((X+1) > &x-13) ) then { + path := "up_left" + if ( (X+1) > &x-13 ) then { + angle := 30 + } + if ( (X+1) > &x-6 ) then { + angle := 60 + } + if ( (X+1) > &x+6 ) then { + angle := 30 + } + } + } + } +} +return +end + + +#these next 4 procedures move the sphere +#and then check for block contact + +procedure GO_UP_RIGHT() + if ( angle = 30 ) then { + DrawImage(X, Y, blank) + Y := Y - 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } + if ( angle = 60 ) then { + DrawImage(X, Y, blank) + Y := Y - 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + Y := Y - 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } +return +end +procedure GO_UP_LEFT() + if ( angle = 30 ) then { + DrawImage(X, Y, blank) + Y := Y - 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } + if ( angle = 60 ) then { + DrawImage(X, Y, blank) + Y := Y - 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + Y := Y - 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } +return +end +procedure GO_DOWN_RIGHT() + if ( angle = 30 ) then { + DrawImage(X, Y, blank) + Y := Y + 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } + if ( angle = 60 ) then { + DrawImage(X, Y, blank) + Y := Y + 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + Y := Y + 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X + 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } +return +end +procedure GO_DOWN_LEFT() + if ( angle = 30 ) then { + DrawImage(X, Y, blank) + Y := Y + 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } + if ( angle = 60 ) then { + DrawImage(X, Y, blank) + Y := Y + 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + DrawImage(X, Y, blank) + Y := Y + 1 + DrawImage(X, Y, sphere) + DrawImage(X, Y, blank) + X := X - 1 + DrawImage(X, Y, sphere) + block_check() + if ( hit = 1 ) then { + fix_blocks() + return + } + } +return +end + + + +#this draws the play fields according to what the levels +#are defined as + +procedure create_blocks() + local x, y, z + + if ( level > 9 ) then { + level := 1 + } + + #different play fields go here + if ( level = 1 ) then { #icon-squared + block_positions := "000000000000000000000000000000000000000100000000110100110111000001010010101110101101110101000000000000000111000001110010100000101001110000011100000000000000010101110110101110101001010000011101100101100000000100000000000000000000000000" + } + if ( level = 2 ) then { #alternate rows + block_positions := "111111111111100000000000001111111111111000000000000011111111111110000000000000111111111111100000000000001111111111111000000000000011111111111110000000000000111111111111100000000000001111111111111000000000000011111111111110000000000000" + } + if ( level = 3 ) then { #alternating columns + block_positions := "101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101101010101010110101010101011010101010101" + } + if ( level = 4 ) then { #heart + block_positions := "000100000100000101000101000100010100010010001010001001000101000100100001000010010000100001000100000001000010000000100001000000010000010000010000001000001000000010001000000000101000000000001000000000000100000000000010000000000000000000" + } + if ( level = 5 ) then { #checker board + block_positions := "101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010" + } + if ( level = 6 ) then { #filled up + block_positions := "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" + } + if ( level = 7 ) then { #diamond and a half + block_positions := "000001110000000001111100000001111111000001111111110001111111111101111111111111011111111111000111111111000001111111000000011111000000000111000000000001000000000001110000000001111100000001111111000001111111110001111111111101111111111111" + } + if ( level = 8 ) then { #misc multiple hits + block_positions := "11111111111111111111111111313131313131311111111111112121212121212X3X2X3X2X3X2X111111111111111111111111113131313131313111111111111112121212121213X2X3X2X3X2X31111111111111111111111111131313131313132121212121212X3X2X3X2X3X2X1111111111111" + } + if ( level = 9 ) then { #throw-rug + block_positions := "21111111111121211111111121112111111121111121111121111111211121111211112121111212111121111211121123211211111223X322111111223X32211111211232112111211112111121211112121111211112111211111112111112111112111111121112111111111212111111111112" + } + + + z := 1 + y := 10 + x := 10 + while not ( y = 208 ) do { + while not ( x = 283 ) do { + if ( block_positions[z] == "0" ) then { + Fg("white") + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "1" ) then { + Fg(blockclr1) + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "2" ) then { + Fg(blockclr2) + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "3" ) then { + Fg(blockclr3) + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "X" ) then { + Fg(invincclr) + FillRectangle(x,y,20,10) + } + z := z + 1 + x := x + 21 + } + x := 10 + y := y + 11 + } +return +end + + +#this checks to see if the sphere contacts an edge +#of a block, if so, it erases the block and changes +#the sphere's direction accordingly +#it also checks if level is finished + +procedure block_check() + local x, y, z, temp, temp2 + z := 1 + y := 10 + x := 10 + while not ( y = 208 ) do { + while not ( x = 283 ) do { + if ( (((X+1)>(x-1))&((X+1)<(x+21)))&(((Y+1)>(y-1))&((Y+1)<(y+11))) ) then { + if ( block_positions[z] == "X" ) then { + hit := 1 + Fg(invincclr) + FillRectangle(x,y,20,10) + if ( path == "up_right" ) then { + if ( ((X+1)=x) ) then { #side hit + path := "up_left" + } + if ( ((Y+1)=(y+10)) ) then { #bottom hit + path := "down_right" + } + if ( ((X+1)=x)&((Y+1)=(y+10)) ) then { #diagonal hit + path := "down_left" + } + } + else { + if ( path == "up_left" ) then { + if ( ((X+1)=(x+20)) ) then { #side hit + path := "up_right" + } + if ( ((Y+1)=(y+10)) ) then { #bottom hit + path := "down_left" + } + if ( ((X+1)=(x+20))&((Y+1)=(y+10)) ) then { #diagonal hit + path := "down_right" + } + } + else { + if ( path == "down_left" ) then { + if ( ((X+1)=(x+20)) ) then { #side hit + path := "down_right" + } + if ( ((Y+1)=y) ) then { #top hit + path := "up_left" + } + if ( ((X+1)=(x+20))&((Y+1)=y) ) then { #diagonal hit + path := "up_right" + } + } + else { + if ( path == "down_right" ) then { + if ( ((X+1)=x) ) then { #side hit + path := "down_left" + } + if ( ((Y+1)=y) ) then { #top hit + path := "up_right" + } + if ( ((X+1)=x)&((Y+1)=y) ) then { #diagonal hit + path := "up_left" + } + } + } + } + } + } + if ( (block_positions[z] == "1") | + (block_positions[z] == "2") | + (block_positions[z] == "3") ) then { + hit := 1 + if ( block_positions[z] == "1" ) then { + Fg("white") + FillRectangle(x,y,20,10) + block_positions[z] := "0" + } + if ( block_positions[z] == "2" ) then { + Fg(blockclr1) + FillRectangle(x,y,20,10) + block_positions[z] := "1" + } + if ( block_positions[z] == "3" ) then { + Fg(blockclr2) + FillRectangle(x,y,20,10) + block_positions[z] := "2" + } + if ( path == "up_right" ) then { + if ( ((X+1)=x) ) then { #side hit + path := "up_left" + } + if ( ((Y+1)=(y+10)) ) then { #bottom hit + path := "down_right" + } + if ( ((X+1)=x)&((Y+1)=(y+10)) ) then { #diagonal hit + path := "down_left" + } + } + else { + if ( path == "up_left" ) then { + if ( ((X+1)=(x+20)) ) then { #side hit + path := "up_right" + } + if ( ((Y+1)=(y+10)) ) then { #bottom hit + path := "down_left" + } + if ( ((X+1)=(x+20))&((Y+1)=(y+10)) ) then { #diagonal hit + path := "down_right" + } + } + else { + if ( path == "down_left" ) then { + if ( ((X+1)=(x+20)) ) then { #side hit + path := "down_right" + } + if ( ((Y+1)=y) ) then { #top hit + path := "up_left" + } + if ( ((X+1)=(x+20))&((Y+1)=y) ) then { #diagonal hit + path := "up_right" + } + } + else { + if ( path == "down_right" ) then { + if ( ((X+1)=x) ) then { #side hit + path := "down_left" + } + if ( ((Y+1)=y) ) then { #top hit + path := "up_right" + } + if ( ((X+1)=x)&((Y+1)=y) ) then { #diagonal hit + path := "up_left" + } + } + } + } + } + #check to see if field is clear for next level + #reset sphere back to below block height + temp := 1 + temp2 := 0 + while ( temp < 244 ) do { + if ( (block_positions[temp] == "1") | + (block_positions[temp] == "2") | + (block_positions[temp] == "3") ) then { + temp2 := 1 + temp := 243 + } + temp := temp + 1 + } + if ( temp2 = 0 ) then { + level := level + 1 + create_blocks() + DrawImage(X,Y,blank) + DrawImage(140, 304, sphere) + X := 140 + Y := 304 + path := "up_right" + } + } + } + z := z + 1 + x := x + 21 + } + x := 10 + y := y + 11 + } +return +end + + +#this is an extra check to make sure the blocks stay completely filled +#when the sphere moves out of a block, the DrawImage(X, Y, blank) +#will draw a white sphere over the old sphere, this fixes blocks +#periodically by being called every block hit in the 4 move sphere procedures + +procedure fix_blocks() + local x, y, z + + z := 1 + y := 10 + x := 10 + while not ( y = 208 ) do { + while not ( x = 283 ) do { + if ( block_positions[z] == "1" ) then { + Fg(blockclr1) + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "2" ) then { + Fg(blockclr2) + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "3" ) then { + Fg(blockclr3) + FillRectangle(x,y,20,10) + } + if ( block_positions[z] == "X" ) then { + Fg(invincclr) + FillRectangle(x,y,20,10) + } + z := z + 1 + x := x + 21 + } + x := 10 + y := y + 11 + } +return +end diff --git a/ipl/gprogs/browser.icn b/ipl/gprogs/browser.icn new file mode 100644 index 0000000..691f418 --- /dev/null +++ b/ipl/gprogs/browser.icn @@ -0,0 +1,137 @@ +############################################################################ +# +# File: browser.icn +# +# Subject: Program to demonstrate file-navigation "dialog" +# +# Author: Ralph E. Griswold +# +# Date: July 10, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: navitrix, vsetup +# +############################################################################ + +link navitrix +link vsetup + +global pat_window +global vidgets + +$define LineLength 75 +$define FileLength 500 + +procedure main() + local root, root_cur + + nav_init() + vidgets := ui() + pat_window := &window + + root := vidgets["root"] + + repeat { + root_cur := case Active() of { + pat_window : root + nav_window : nav_root + } + ProcessEvent(root_cur, , shortcuts) + case nav_state of { + &null : next + "Okay" : process_file() + } + nav_state := &null + } + +end + +procedure process_file() + local input, file_list + static list_vidget + + initial list_vidget := vidgets["list"] + + if nav_file[-1] == "/" then { # directory + chdir(nav_file) + nav_refresh() + } + + else { # "plain" file + input := open(nav_file) | { + Notice("Cannot open " || image(nav_file) || ".") + fail + } + file_list := [] + every put(file_list, left(entab(!input), LineLength)) \ FileLength + VSetItems(list_vidget, file_list) + close(input) + WAttrib(nav_window, "canvas=hidden") + } + + return + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "find @F" : find_file() + "quit @Q" : exit() + } + + return + +end + +procedure list_cb(vidget, value) + + if /value then return # deselection; no action + + return + +end + +procedure find_file() + + WAttrib(nav_window, "canvas=normal") + + return + +end + +procedure shortcuts(e) + + if &meta then case map(e) of { + "f" : find_file() + "q" : exit() + } + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=587,402", "bg=pale gray", "label=Browser"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,587,402:Browser",], + ["file:Menu:pull::0,3,36,21:File",file_cb, + ["find @F","quit @Q"]], + ["list:List:r::17,44,557,343:",list_cb], + ["menubar:Line:::0,26,585,26:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/ca21.icn b/ipl/gprogs/ca21.icn new file mode 100644 index 0000000..7aded56 --- /dev/null +++ b/ipl/gprogs/ca21.icn @@ -0,0 +1,122 @@ +############################################################################ +# +# File: ca21.icn +# +# Subject: Program to investigate cellular automata +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays the time-sequence development on one-dimensional +# cellular automata in which the state of a cell depends only on the +# two cells adjacent to it -- 2,1 automata. +# +# See "Theory and Applications of Cellular Automata", Stephan Wolfram, +# World Scientific, 1986 for an explanation for the method and rule +# encoding. +# +# The options supported are: +# +# -r i rule i, default 110 +# -w i width (number of cells), default 200 +# -h i height (number of time steps), default width +# -s seed first row at random with <= width / 2 cells +# -R randomize the run +# -e s initialize first row with seeds at positions generated +# by Icon expression e. +# -i s save final image in file named s; default no image +# -H use hidden window; if no image file specified, ca21.gif +# is used +# +# The -e option is powerful but somewhat strange. For example, to +# seed every other cell in the first row, use +# +# -e 'seq(1,2') +# +# which generates 1, 3, 5, 7, ... and seeds those cells (cells are +# numbered starting at 1). +# +############################################################################ +# +# Requires: Version 9 graphics; system(), pipes, /tmp for -e option +# +############################################################################ +# +# Links: evallist, genrfncs, options, convert, random, wopen +# +############################################################################ + +link evallist +link genrfncs +link options +link convert +link random +link wopen + +procedure main(args) + local opts, rule, bits, binary, i, j, phi, width, height, v, old, new + local ilist, name, canvas + + opts := options(args, "w+h+r+sRe:i:H") + + width := \opts["w"] | 200 + height := \opts["h"] | width + rule := \opts["r"] | 110 + if \opts["R"] then randomize() + name := \opts["i"] + if \opts["H"] then { + canvas := "canvas=hidden" + /name := "ca21.gif" + } + else canvas := "canvas=normal" + + WOpen(canvas, "width=" || width, "height=" || height) | + stop("*** cannot open window") + + bits := create !right(exbase10(rule, 2), 8, "0") + binary := create ("1" | "0") || ("1" | "0") || ("1" | "0") + + phi := table() + + while phi[@binary] := @bits + + new := repl("0", width) + + if \opts["e"] then { + ilist := evallist(opts["e"], width, "seqfncs") | + stop("invalid initialization expression") + every i := !ilist do { + new[i] := "1" + DrawPoint(i- 1, 0) + } + } + else if \opts["s"] then { # random, scattered seeds + every 1 to width / 2 do { + new[i := ?width] := "1" + DrawPoint(i - 1, 0) + } + } + else { + new[width / 2] := "1" # single, centered seed + DrawPoint(width / 2 - 1, 0) + } + + every j := 2 to height do { + old := new + new := repl("0", width) + every i := 2 to width - 1 do { + new[i] := v := phi[old[i - 1 : i + 2]] + if v == "1" then DrawPoint(i - 1, j - 1) + } + } + + WriteImage(\name) + +end diff --git a/ipl/gprogs/calib.icn b/ipl/gprogs/calib.icn new file mode 100644 index 0000000..6c97694 --- /dev/null +++ b/ipl/gprogs/calib.icn @@ -0,0 +1,95 @@ +############################################################################ +# +# File: calib.icn +# +# Subject: Program to calibrate color monitor +# +# Author: Gregg M. Townsend +# +# Date: May 31, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# The nonlinearity of a color display is often characterized by a +# "gamma correction" value; calib provides a crude method for determining +# this value for a particular monitor. It displays two rectangles: one +# formed of alternating black and white scanlines and one formed of a +# single, solid color. Move the slider until they match; the number +# displayed above the slider is the gamma-correction factor. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, evmux, graphics, options, optwindw, slider +# +############################################################################ + +link button +link evmux +link graphics +link options +link optwindw +link slider + +record boxdata(win, color, button) + +procedure main(args) + local opts, w, h, m, boxwidth, sliderwidth, textheight + local win, box, boxwin, boxcolor, y + local mingamma, defaultgamma, maxgamma + + opts := options(args, winoptions()) + /opts["W"] := 500 + /opts["H"] := 400 + /opts["M"] := -1 + win := optwindow(opts, "cursor=off", "echo=off") + w := opts["W"] + h := opts["H"] + m := opts["M"] + textheight := 20 + sliderwidth := 20 + boxwidth := (w - 3 * m) / 2 + if (h + 1) % 2 = 1 then + h -:= 1 + + mingamma := 1.0 + defaultgamma := WAttrib(win, "gamma") + maxgamma := 5.0 + + boxwin := Clone(win) + Fg(boxwin, "black") + Bg(boxwin, "white") + EraseArea(boxwin, m, m, boxwidth, h) + every y := m to h + m by 2 do + DrawLine(boxwin, m, y, m + boxwidth, y) + boxcolor := NewColor(boxwin) | stop("can't allocate a mutable color") + + # we use a do-nothing button for displaying the gamma value (!) + box := boxdata(boxwin, boxcolor, + button(win, "", &null, 0, m+w-sliderwidth, m, sliderwidth, textheight)) + setgamma(win, box, defaultgamma) + + Fg(boxwin, boxcolor) + FillRectangle(boxwin, m + boxwidth, m, boxwidth, h) + quitsensor(win) + slider(win, setgamma, box, + m + w - sliderwidth, 2 * m + textheight, sliderwidth, h - textheight - m, + mingamma, defaultgamma, maxgamma) + evmux(win) +end + +procedure setgamma(win, box, gamma) + local v + + buttonlabel(box.button, left(gamma + .05, 3)) + WAttrib(box.win, "gamma=" || gamma) + Color(box.win, box.color, "gray") + return +end diff --git a/ipl/gprogs/cameleon.icn b/ipl/gprogs/cameleon.icn new file mode 100644 index 0000000..e616d20 --- /dev/null +++ b/ipl/gprogs/cameleon.icn @@ -0,0 +1,300 @@ +############################################################################ +# +# File: cameleon.icn +# +# Subject: Program to allow user to change colors in an image +# +# Author: Ralph E. Griswold +# +# Date: May 19, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This application allows the user to change selected color in an image. +# The colors are displayed in a palette on the application window. +# Clicking on one brings up a color dialog in which the color can be +# adjusted. +# +# The keyboard shortcuts are: +# +# @O open image File menu +# @Q quit the application File menu +# @R revert to original colors Colors menu +# @S save image File menu +# +# Note: "cameleon" is a variant spelling of "chameleon". +# +############################################################################ +# +# Requires: Version 9 graphics and mutable colors. +# +############################################################################ +# +# Links: graphics, interact, numbers, tables +# +############################################################################ + +link graphics +link interact +link numbers +link tables + +global cellsize # size of palette cell +global colors # mutable color list +global count # table of pixel counts +global image_window # window for user image +global mutant # image with mutable colors +global orig_colors # list of original colors +global palette # color selection palette +global panel # palette window +global pixels # number of pixels in image window +global x_pos # target location for mutant window +global y_pos + + +$define ColorRows 8 # number of palette rows +$define ColorCols 16 # number of palette columns + +procedure main() + local atts, vidgets + + atts := ui_atts() + put(atts, "posx=0", "posy=0") + + (WOpen ! atts) | stop("*** cannot open application window") + + vidgets := ui() + + x_pos := WAttrib("width") + 3 * WAttrib("posx") + y_pos := WAttrib("posy") + + palette := vidgets["palette"] + + cellsize := palette.uw / ColorCols + + panel := Clone("bg=black", "dx=" || palette.ux, "dy=" || palette.uy) + Clip(panel, 0, 0, palette.uw, palette.uh) + + clear_palette() + + GetEvents(vidgets["root"], , shortcuts) + +end + +# Set up empty palette grid + +procedure clear_palette() + local x, y + + Fg(panel, "black") + EraseArea(panel) + WAttrib(panel, "fillstyle=textured") + Pattern(panel, "checkers") + Bg(panel, "very dark gray") + + every x := 1 + (0 to ColorCols) * cellsize do + every y := 1 + (0 to ColorRows) * cellsize do + FillRectangle(panel, x, y, cellsize - 1, cellsize - 1) + + WAttrib(panel, "fillstyle=solid") + Bg(panel, "black") + + return + +end + +# Handle File menu + +procedure file_cb(vidget, value) + + case value[1] of { + "open @O" : image_open() + "quit @Q" : quit() + "revert @R" : image_revert() + "save @S" : snapshot(mutant) + } + + return + +end + +# Open new image + +procedure image_open() + local i, x, y + + WClose(\image_window) + + repeat { + if OpenDialog("Open image:") == "Cancel" then fail + image_window := WOpen("canvas=hidden", "image=" || dialog_value) | { + Notice("Cannot open image.") + next + } + break + } + + mutate(image_window) | fail + + Raise() # bring application window to front + + colors := vallist(copy(orig_colors)) + + clear_palette() + + i := 0 + + every y := 1 + (0 to ColorRows - 1) * cellsize do + every x := 1 + (0 to ColorCols - 1) * cellsize do { + Fg(panel, colors[i +:= 1]) | break break + FillRectangle(panel, x, y, cellsize - 1, cellsize - 1) + } + + return + +end + +# Save current image + +procedure image_save() + + snapshot(\mutant) + + return + +end + +# Restore original image colors + +procedure image_revert() + local old, color + + every old := key(orig_colors) do { + color := orig_colors[old] + Color(panel, color, old) + } + + return + +end + +# Get mutable colors and window from image + +procedure mutate() + local c, width, height, n, x, y + + WClose(\mutant) + + orig_colors := table() + count := table(0) + + width := WAttrib(image_window, "width") + height := WAttrib(image_window, "height") + + pixels := width * height + + mutant := WOpen("width=" || width, "height=" || height, + "posx=" || x_pos, "posy=" || y_pos) | { + Notice("Cannot open image_window for mutant colors.") + fail + } + + every y := 0 to height - 1 do { + x := 0 + every c := Pixel(image_window, 0, y, width, 1) do { + if not(n := \orig_colors[c]) then { + orig_colors[c] := n := NewColor(c) | { + Notice("Cannot get mutable color.") + WClose(mutant) + fail + } + } + count[n] +:= 1 + Fg(mutant, n) + DrawPoint(mutant, x, y) + x +:= 1 + } + } + + return + +end + +# Handle callbacks on palette + +procedure palette_cb(vidget, e, x, y) + local color, new + + if e === (&lpress | &mpress | &rpress) then { + color := Pixel(x, y, 1, 1) # get pixel color + if not integer(color) then fail # not a mutable color + new := Color(panel, color) # get color specification + if ColorDialog( + "Adjust color (" || count[color] || " pixels, " || + frn((100.0 * count[color]) / pixels, , 2) || "%):", + Color(panel, color), + track, + color + ) == "Okay" then new := dialog_value + Color(panel, color, new) + Color(mutant, color, new) + } + + return + +end + +# Quit the application + +procedure quit() + + snapshot(\mutant) + + exit() + +end + +# Handle keyboard shortcuts + +procedure shortcuts(e) + + if &meta then case(map(e)) of { + "o" : image_open() + "q" : quit() + "r" : image_revert() + "s" : image_save() + } + + return + +end + +# Track the color in the color dialog + +procedure track(color, s) + + Color(panel, color, s) + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=355,225", "bg=pale gray", "label=chameleon"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,355,225:chameleon",], + ["file:Menu:pull::1,0,36,21:File",file_cb, + ["open @O","save @S","revert @R","quit @Q"]], + ["menubar:Line:::0,21,357,21:",], + ["palette:Rect:invisible::19,41,320,160:",palette_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/chernoff.icn b/ipl/gprogs/chernoff.icn new file mode 100644 index 0000000..27810ef --- /dev/null +++ b/ipl/gprogs/chernoff.icn @@ -0,0 +1,169 @@ +############################################################################ +# +# File: chernoff.icn +# +# Subject: Program to imitate a Chernoff face +# +# Author: Jon Lipp +# +# Date: August 14, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays a Chernoff face. +# +############################################################################ +# +# Links: options, vidgets, vscroll, vbuttons, wopen, xcompat +# +############################################################################ + +link options +link vidgets, vscroll, vbuttons +link wopen +link xcompat + +global FH + +procedure main(args) +local opts, font, wid, h +local root, win, s1, s2, s3, s4, s5 + + opts := options(args, "f:wh") + font := \opts["f"] + wid := \opts["w"] + h := \opts["h"] + + win := WOpen("label=popup dialogs demo", + "size=" || (\wid | 425) || "," || (\h | 325)) | + stop("*** can't open window") + + root := Vroot_frame(win) + + FH := WAttrib(win, "fheight") + + s1 := Vhoriz_scrollbar(root, 0, 50, win, eyes, 1, 90, , 10, 99, 1) + s2 := Vhoriz_scrollbar(root, 0, 100, win, pupils, 2, 90, , 10, 99, 1) + s3 := Vhoriz_scrollbar(root, 0, 150, win, nose, 2, 90, , 0, 25, 1) + s4 := Vhoriz_scrollbar(root, 0, 200, win, smile, 2, 90, , 47, 32, 1) + s5 := Vhoriz_scrollbar(root, 0, 250, win, face, 2, 90, , 250, 300, 1) + +# Vpane(root, 100, 10, win, , , 200, 200) + + VResize(root) + put_label(root, s1, "eyes") + put_label(root, s2, "pupils") + put_label(root, s3, "nose") + put_label(root, s4, "smile") + put_label(root, s5, "face") + eyes(s1.thumb, s1.callback.value) + pupils(s2.thumb, s2.callback.value) + nose(s3.thumb, s3.callback.value) + smile(s4.thumb, s4.callback.value) + face(s5.thumb, s5.callback.value) + + GetEvents(root, quit) +end + + +procedure quit(e) + if e === "q" then stop() +end + +procedure write_val(vid, val) + GotoXY(vid.win, vid.ax-10, vid.ay-5) + writes(vid.win, val||" ") +end + +procedure put_label(root, sc, str) + local x, l + + l := TextWidth(root.win, str) + x := sc.ax+sc.aw-l + VDraw(Vmessage(root, x, sc.ay-5-FH, root.win, str)) +end + +procedure face(vid, val) + local x1, y, x + static faceval, ox1, oy + + write_val(vid, val) + x1 := 250 - val/2 + y := 150 - val/2 + rev_on(vid.win) + XDrawArc(vid.win, \ox1, \oy, \faceval, \faceval) + rev_off(vid.win) + XDrawArc(vid.win, x1, y, val, val) + faceval := val + ox1 := x1; oy := y +end + +procedure eyes(vid, val) + local x1, x2, y + static eyeval, ox1, ox2, oy + + write_val(vid, val) + x1 := 200 - val/2 + x2 := 300 - val/2 + y := 100 - val/2 + rev_on(vid.win) + XDrawArc(vid.win, \ox1, \oy, \eyeval, \eyeval) + XDrawArc(vid.win, \ox2, \oy, \eyeval, \eyeval) + rev_off(vid.win) + XDrawArc(vid.win, x1, y, val, val) + XDrawArc(vid.win, x2, y, val, val) + eyeval := val + ox1 := x1; ox2 := x2; oy := y +end + +procedure pupils(vid, val) + local x1, x2, y + static pupilval, ox1, ox2, oy + + write_val(vid, val) + x1 := 200 - val/2 + x2 := 300 - val/2 + y := 100 - val/2 + rev_on(vid.win) + XFillArc(vid.win, \ox1, \oy, \pupilval, \pupilval) + XFillArc(vid.win, \ox2, \oy, \pupilval, \pupilval) + rev_off(vid.win) + XFillArc(vid.win, x1, y, val, val) + XFillArc(vid.win, x2, y, val, val) + pupilval := val + ox1 := x1; ox2 := x2; oy := y +end + +procedure smile(vid, val) + static oldsmile + + write_val(vid, val) + rev_on(vid.win) + XDrawArc(vid.win, 185, 190, 130, 40, \oldsmile*360, (48-\oldsmile)*2*360) + rev_off(vid.win) + XDrawArc(vid.win, 185, 190, 130, 40, val*360, (48-val)*2*360) + oldsmile := val +end + +procedure nose(vid, val) + static oldnose + + write_val(vid, val) + rev_on(vid.win) + DrawLine(vid.win, 250, 140, 275, 180+\oldnose, 250, 190) + rev_off(vid.win) + DrawLine(vid.win, 250, 140, 275, 180+val, 250, 190) + oldnose := val + +end + +procedure rev_on(win) + WAttrib(win, "reverse=on", "linewidth=3") +end +procedure rev_off(win) + WAttrib(win, "reverse=off", "linewidth=1") +end diff --git a/ipl/gprogs/clrs2pdb.icn b/ipl/gprogs/clrs2pdb.icn new file mode 100644 index 0000000..e798f17 --- /dev/null +++ b/ipl/gprogs/clrs2pdb.icn @@ -0,0 +1,56 @@ +############################################################################ +# +# File: clrs2pdb.icn +# +# Subject: Program to create custom palettes from color lists +# +# Author: Ralph E. Griswold +# +# Date: October 29, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program builds a palette database from color lists. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, palettes, xcode +# +############################################################################ + +link basename +link palettes +link xcode + +global PDB_ + +procedure main(args) + local file, input, clist, line, name + + every file := !args do { + input := open(file) | { + write(&errout, "*** cannot open ", image(file)) + next + } + name := basename(file, ".clr") + clist := [] + while line := read(input) do { + line ?:= tab(upto('\t')) + put(clist, line) + } + close(input) + makepalette(name, clist) | + write(&errout, "*** could not make palette from ", image(file)) + } + + xencode(PDB_, &output) + +end diff --git a/ipl/gprogs/coloralc.icn b/ipl/gprogs/coloralc.icn new file mode 100644 index 0000000..0f76a86 --- /dev/null +++ b/ipl/gprogs/coloralc.icn @@ -0,0 +1,193 @@ +############################################################################ +# +# File: coloralc.icn +# +# Subject: Program to test color allocation +# +# Author: Gregg M. Townsend +# +# Date: February 6, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# coloralc tests how many fixed and/or mutable colors can be allocated. +# The two sets of pushbuttons allocate 1, 8, or 32 randomly chosen colors +# of the selected type. New colors are arrayed on the display using +# squares for fixed colors and discs for mutable colors. When no more +# colors can be created, no more squares or discs will appear. +# +# Clicking on a color with the left mouse button selects it as the +# current color; the current color can be drawn on the screen by moving +# the mouse with the left button down. +# +# Clicking on a mutable color (a disc) with the right mouse mutton +# changes it to a new random color. There is also a pushbutton that +# changes all mutable colors simultaneously. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, button, evmux, random, graphics +# +############################################################################ + +link options +link optwindw +link button +link evmux +link random +link graphics + +record square(x, y, w, p, n) # a color square (or disc) + +global win, opts, m, w, h, s, bw, bh # main window and dimensions +global sq, nw, nh # square list and layout counts + +global brushwin, brushproc # current drawing window and procedure + + + +# main program + +procedure main(args) + + nw := 16 # number of squares wide + nh := 16 # number of squares high + s := 32 # square size + m := 4 # margin (gap) + bw := 68 # button width + bh := 20 # button height + + opts := options(args, winoptions()) + /opts["W"] := nw * (m + s) + bw + /opts["H"] := nh * (m + s) - m + /opts["M"] := m + win := optwindow(opts, "cursor=off", "echo=off") + m := opts["M"] # get obtained window dimensions + h := opts["H"] + w := opts["W"] + s := (w - bw - nw * m) / nw # calc size of each square + s <:= (h - (nh - 1) * m) / nh + + quitsensor(win) # set up sensors + sensor(win, &lpress, dobrush) + sensor(win, &ldrag, dobrush) + sensor(win, 'f', fixc, 1) + sensor(win, 'F', fixc, 8) + sensor(win, 'm', mutc, 1) + sensor(win, 'M', mutc, 8) + sensor(win, 'Aa', mutall) + buttonrow(win, m, m, bw, bh, 0, m + bh, + "1 fixed", fixc, 1, + "8 fixed", fixc, 8, + "32 fixed", fixc, 32, + ) + buttonrow(win, m, m + 4 * (bh + m), bw, bh, 0, m + bh, + "1 mutable", mutc, 1, + "8 mutable", mutc, 8, + "32 mutable", mutc, 32, + ) + buttonrow(win, m, m + 8 * (bh + m), bw, bh, 0, m + bh, + "mutate all", mutall, 0, + "quit", argless, exit, + ) + + sq := [] # init square list and procs + brushwin := win + brushproc := DrawRectangle + + randomize() + evmux(win) # loop processing events +end + + + +# fixc(w, n) -- allocate n new fixed colors + +procedure fixc(w, n) + local q + every 1 to n do { + q := newsquare(w, FillRectangle) | fail + Fg(q.w, ?65535 || "," || ?65535 || "," || ?65535) | {pull(sq); fail} + FillRectangle(q.w, q.x, q.y, s, s) # interior (random new color) + DrawRectangle(win, q.x, q.y, s, s) # outline (standard) + sensor(win, &lpress, setbrush, q, q.x, q.y, s, s) + } + return +end + + + +# mutc(w, n) -- allocate n new mutable colors + +procedure mutc(w, n) + local q + every 1 to n do { + q := newsquare(w, FillArc) | fail + q.n := NewColor(q.w, ?65535 || "," || ?65535 || "," || ?65535) | + {pull(sq); fail} + Fg(q.w, q.n) + FillArc(q.w, q.x, q.y, s, s) + DrawArc(win, q.x, q.y, s, s) + sensor(win, &lpress, setbrush, q, q.x, q.y, s, s) + sensor(win, &rpress, randmut, q, q.x, q.y, s, s) + } + return +end + + +# newsquare(w, p) -- alc next square, init for proc p, return record + +procedure newsquare(w, p) + local x, y, q + *sq < nw * nh | fail + x := m + bw + m + (m + s) * (*sq % nw) + y := m + (m + s) * (*sq / nw) + q := square(x, y, Clone(w), p) | fail + put(sq, q) + return q +end + + +# randmut(w, q) -- randomly mutate square q to a new color + +procedure randmut(w, q) + Color(q.w, \q.n, ?65535 || "," || ?65535 || "," || ?65535) + return +end + + +# mutall(w) -- randomly mutate *all* the squares + +procedure mutall(w) + local args + args := [w] + every put(args, \(!sq).n) do + put(args, ?65535 || "," || ?65535 || "," || ?65535) + if *args > 1 then + Color ! args +end + + +# setbrush(w, q) -- set the paintbrush to the values for square q + +procedure setbrush(w, q) + brushwin := q.w + brushproc := q.p + return +end + + +# dobrush(w, dummy, x, y) -- call the brush procedure at location (x, y) + +procedure dobrush(w, dummy, x, y) + brushproc(brushwin, x - s / 4, y - s / 4, s / 2, s / 2) + return +end diff --git a/ipl/gprogs/colormap.icn b/ipl/gprogs/colormap.icn new file mode 100644 index 0000000..076abbd --- /dev/null +++ b/ipl/gprogs/colormap.icn @@ -0,0 +1,119 @@ +############################################################################ +# +# File: colormap.icn +# +# Subject: Program to display palette from color list +# +# Author: Ralph E. Griswold +# +# Date: November 17, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program shows the colors given color list files given on the +# command line. +# +# colormap will display color lists with more than 256 entries but, +# of course, it cannot display more than 256 different colors (if that +# many). +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: colrlist, drawcolr, interact, io, vsetup +# +############################################################################ + +$define CellWidth 20 +$define Cells 16 + +link colrlist +link drawcolr +link interact +link io +link vsetup + +global colors + +procedure main() + local vidgets + + vidgets := ui() + + GetEvents(vidgets["root"], , shortcuts) + +end + +procedure file_cb(vidgets, value) + + case value[1] of { + "load @L": load_colors() + "snapshot @S": snapshot(colors) + "quit @Q": exit() + } + + return + +end + +procedure reload_cb() + + return + +end + +procedure load_colors() + local clist + static file + + initial file := "" + + repeat { + if OpenDialog("Specify color list file:", file) == "Cancel" then fail + clist := colrlist(dialog_value) | { + Notice("Cannot process color list " || image(dialog_value) || ".") + next + } + WClose(\colors) + colors := draw_colors(clist) + Raise() + return + } + +end + +procedure shortcuts(e) + + if &meta then case map(e) of { + "l": load_colors() + "q": exit() + "r": reload_cb() + "s": snapshot() + } + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=197,288", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,197,288:",], + ["file:Menu:pull::1,0,36,21:File",file_cb, + ["load @L","snapshot @S","quit @Q"]], + ["line1:Line:::0,24,197,24:",], + ["reload:Button:regular::26,56,49,20:reload",reload_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/colorup.icn b/ipl/gprogs/colorup.icn new file mode 100644 index 0000000..a58ce4a --- /dev/null +++ b/ipl/gprogs/colorup.icn @@ -0,0 +1,133 @@ +############################################################################ +# +# File: colorup.icn +# +# Subject: Program to produce a weave structure from unravel data +# +# Author: Ralph E. Griswold +# +# Date: April 18, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################# +# +# Input is expected to be the output of unravel -r. +# +############################################################################# +# +# This program takes information from a image solved by unravel.icn to +# produce a draft. +# +# The option -o i determines how optional choices at intersections are +# handled: +# +# 0 random (default) +# 1 warp +# 2 weft +# 3 alternating +# +############################################################################ +# +# Links: numbers, options, weavutil, patxform, patutils, xcode +# +############################################################################ + +link numbers +link options +link patutils +link patxform +link weavutil +link xcode +link ximage + +procedure main(args) + local warp, weft, pattern, rows, i, j, count, opts + local threading, treadling, color_list, colors, choice + local symbols, symbol, drawdown, draft, warp_colors, weft_colors, pixels + + opts := options(args, "o+") + + choice := opts["o"] | 0 + + (warp := read() & weft := read() & pattern := read()) | + stop("*** short file") + + pixels := real(*pattern) + + colors := warp ++ weft + + color_list := [] + + every put(color_list, PaletteColor("c1", !colors)) + + warp_colors := [] + + every put(warp_colors, upto(!warp, colors)) + + weft_colors := [] + + every put(weft_colors, upto(!weft, colors)) + + drawdown := [] + + pattern ? { + while put(drawdown, move(*warp)) + } + + count := 0 + + every i := 1 to *weft do { # row + every j := 1 to *warp do { # column + if weft[i] == warp[j] then { # option point + count +:= 1 + drawdown[i, j] := case choice of { + 0 : ?2 - 1 # random + 1 : "1" # warp + 2 : "0" # weft + 3 : if count % 2 = 0 then "1" else "2" # alternative + } + } + else if drawdown[i, j] == weft[i] then drawdown[i, j] := "0" + else drawdown[i, j] := "1" + } + } + + treadling := analyze(drawdown) + drawdown := protate(drawdown, "cw") + threading := analyze(drawdown) + + symbols := table("") + + every pattern := !treadling.patterns do { + symbol := treadling.rows[pattern] + symbols[symbol] := repl("0", *threading.rows) + pattern ? { + every i := upto('1') do + symbols[symbol][threading.sequence[i]] := "1" + } + } + + symbols := sort(symbols, 3) + rows := [] + + while get(symbols) do + put(rows, get(symbols)) + + draft := isd() + + draft.name := "colorup" + draft.threading := threading.sequence + draft.treadling := treadling.sequence + draft.warp_colors := warp_colors + draft.weft_colors := weft_colors + draft.color_list := color_list + draft.shafts := *threading.rows + draft.treadles := *treadling.rows + draft.tieup := rows + + xencode(draft, &output) + +end diff --git a/ipl/gprogs/colorwif.icn b/ipl/gprogs/colorwif.icn new file mode 100644 index 0000000..24d1f35 --- /dev/null +++ b/ipl/gprogs/colorwif.icn @@ -0,0 +1,232 @@ +############################################################################ +# +# File: colorwif.icn +# +# Subject: Program to produce a WIF from unravel data +# +# Author: Ralph E. Griswold +# +# Date: April 24, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################# +# +# Input is expected to be the output of unravel -r. +# +############################################################################# +# +# This program takes information from a image solved by unravel.icn to +# produce a draft. +# +# The option -o i determines how optional choices at intersections are +# handled: +# +# 0 random (default) +# 1 warp +# 2 weft +# 3 alternating +# +############################################################################ +# +# Links: numbers, options, weavutil, patxform, patutils +# +############################################################################ + +link numbers +link options +link patutils +link patxform + +record analysis(rows, sequence, patterns) + +procedure main(args) + local warp, weft, pattern, rows, i, j, count, opts + local threading, treadling, color_list, colors, choice + local symbols, symbol, drawdown, draft, warp_colors, weft_colors, pixels + + opts := options(args, "o+") + + choice := opts["o"] | 0 + + (warp := read() & weft := read() & pattern := read()) | + stop("*** short file") + + pixels := real(*pattern) + + colors := warp ++ weft + + color_list := [] + + every put(color_list, PaletteColor("c1", !colors)) + + warp_colors := [] + + every put(warp_colors, upto(!warp, colors)) + + weft_colors := [] + + every put(weft_colors, upto(!weft, colors)) + + drawdown := [] + + pattern ? { + while put(drawdown, move(*warp)) + } + + count := 0 + + every i := 1 to *weft do { # row + every j := 1 to *warp do { # column + if weft[i] == warp[j] then { # option point + count +:= 1 + drawdown[i, j] := case choice of { + 0 : ?2 - 1 # random + 1 : "1" # warp + 2 : "0" # weft + 3 : if count % 2 = 0 then "1" else "2" # alternative + } + } + else if drawdown[i, j] == weft[i] then drawdown[i, j] := "0" + else drawdown[i, j] := "1" + } + } + + treadling := analyze(drawdown) + drawdown := protate(drawdown, "cw") + threading := analyze(drawdown) + + symbols := table("") + + every pattern := !treadling.patterns do { + symbol := treadling.rows[pattern] + symbols[symbol] := repl("0", *threading.rows) + pattern ? { + every i := upto('1') do + symbols[symbol][threading.sequence[i]] := "1" + } + } + + symbols := sort(symbols, 3) + rows := [] + + while get(symbols) do + put(rows, get(symbols)) + + # Now output the WIF. + + write("[WIF]") + write("Version=1.1") + write("Date=" || &dateline) + write("Developers=ralph@cs.arizona.edu") + write("Source Program=colorwif.icn") + + write("[CONTENTS]") + write("Color Palette=yes") + write("Text=yes") + write("Weaving=yes") + write("Tieup=yes") + write("Color Table=yes") + write("Threading=yes") + write("Treadling=yes") + write("Warp colors=yes") + write("Weft colors=yes") + write("Warp=yes") + write("Weft=yes") + + write("[COLOR PALETTE]") + write("Entries=", *color_list) + write("Form=RGB") + write("Range=0," || 2 ^ 16 - 1) + + write("[TEXT]") + write("Title=example") + write("Author=Ralph E. Griswold") + write("Address=5302 E. 4th St., Tucson, AZ 85711-2304") + write("EMail=ralph@cs.arizona.edu") + write("Telephone=520-881-1470") + write("FAX=520-325-3948") + + write("[WEAVING]") + write("Shafts=", *threading.rows) + write("Treadles=", *treadling.rows) + write("Rising shed=yes") + + write("[WARP]") + write("Threads=", *threading.sequence) + write("Units=Decipoints") + write("Thickness=10") + + write("[WEFT]") + write("Threads=", *treadling.sequence) + write("Units=Decipoints") + write("Thickness=10") + + # These are provided to produce better initial configurations when + # WIFs are imported to some weaving programs. + + write("[WARP THICKNESS]") + write("[WEFT THICKNESS]") + + write("[COLOR TABLE]") + every i := 1 to *color_list do + write(i, "=", ColorValue(color_list[i])) + + write("[WARP COLORS]") + every i := 1 to *warp_colors do + write(i, "=", warp_colors[i]) + + write("[WEFT COLORS]") + every i := 1 to *weft_colors do + write(i, "=", weft_colors[i]) + + write("[THREADING]") + every i := 1 to *threading.sequence do + write(i, "=", threading.sequence[i]) + + write("[TREADLING]") + every i := 1 to *treadling.sequence do + write(i, "=", treadling.sequence[i]) + + write("[TIEUP]") + every i := 1 to *rows do + write(i, "=", tromp(rows[i])) + +end + +procedure tromp(treadle) + local result + + result := "" + + treadle ? { + every result ||:= upto("1") || "," + } + + return result[1:-1] + +end + +procedure analyze(drawdown) + local sequence, rows, row, count, patterns + + sequence := [] + patterns := [] + + rows := table() + + count := 0 + + every row := !drawdown do { + if /rows[row] then { + rows[row] := count +:= 1 + put(patterns, row) + } + put(sequence, rows[row]) + } + + return analysis(rows, sequence, patterns) + +end diff --git a/ipl/gprogs/colrbook.icn b/ipl/gprogs/colrbook.icn new file mode 100644 index 0000000..01313ca --- /dev/null +++ b/ipl/gprogs/colrbook.icn @@ -0,0 +1,179 @@ +############################################################################ +# +# File: colrbook.icn +# +# Subject: Program to show the named colors +# +# Author: Gregg M. Townsend +# +# Date: December 1, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# colrbook is a mouse-driven program for choosing named colors. +# Along the left are 24 equally spaced hues plus black, gray, white, +# brown, violet, and pink. Click on any of these to see the twenty +# colors that are possible by adding lightness and saturation +# modifiers to the particular hue. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, evmux, graphics +# +############################################################################ + +link button +link evmux +link graphics + +$define BevelWidth 2 +$define WindowMargin 10 + +$define HEADER 20 # height of header area (not incl. margin) +$define FOOTER 20 # height of footer area (not incl. margin) + +$define TSIZ 12 # hue triangle size +$define HUEW 20 # hue width +$define HGAP 1 # hue gap + +$define LEFT (m+TSIZ+HUEW+labw) # total space to left of grid and its margin + + +global cwin, huelist, sats, lgts, colrs, fillargs +global labw, leftx, w, h, m + +procedure main(args) + local x, y, dx, dy, cw, ch + local i, j, ij, hue, r + + lgts := ["pale", "light", "medium", "dark", "deep"] + sats := ["weak", "moderate", "strong", "vivid"] + colrs := table() + fillargs := table() + + Window("size=500,350", "font=Helvetica,bold,12", args) + cwin := Clone() + m := WindowMargin + w := WAttrib("width") - 2 * m + h := WAttrib("height") - 2 * m + labw := TextWidth("medium") + 3 * m # label area width + leftx := m + TSIZ + HUEW + labw # space to left of grid and its margin + + dx := (w - leftx + m) / *sats + dy := (h - HEADER - FOOTER + m) / *lgts + cw := dx - m + ch := dy - m + + inithues() + + every i := 1 to *sats do + every j := 1 to *lgts do { + ij := i || j + x := leftx + dx * i - cw + y := HEADER + dy * j - ch + BevelRectangle(x, y, cw, ch, -BevelWidth) + fillargs[ij] := [cwin, x + BevelWidth, y + BevelWidth, + cw - 2 * BevelWidth, ch - 2 * BevelWidth] + if Fg(cwin, colrs[ij] := NewColor("gray")) then # may fail + FillRectangle ! fillargs[ij] + } + every i := 1 to *sats do { + GrooveRectangle(leftx + m + dx * (i - 1), m / 2, dx - m, HEADER) + CenterString(leftx + dx * i - cw / 2, m / 2 + HEADER / 2, sats[i]) + } + every j := 1 to *lgts do { + GrooveRectangle(leftx, HEADER + dy*j - ch/2 - HEADER/2, -labw + m, HEADER) + RightString(leftx - m, HEADER + dy*j - ch/2, lgts[j]) + } + + # define sensors + button(&window, "QUIT", argless, exit, m+TSIZ+HUEW+m, m, labw-2*m, HEADER) + sensor(&window, &lpress, hueclick, r, m, m, TSIZ + HUEW, h) + quitsensor(&window) + + # initialize to "gray" hues using an artificial event + Enqueue(&lrelease) + hueclick(&window, 0, m, m + integer((*huelist - 4.5) / *huelist * h)) + + # enter event loop + evmux(&window) +end + +procedure hueclick(win, arg, x, y) + local hue, e, n, i, j + + e := &ldrag + while e ~=== &lrelease do { + if e === &ldrag then { + n := (*huelist * (y - m + HGAP / 2)) / h + 1 + if 0 < n <= *huelist then { + hue := huelist[n] + EraseArea(m, m - TSIZ / 2, TSIZ + 1, h + TSIZ) + y := m - HGAP + integer((n - 0.5) * (h + HGAP) / *huelist) + BevelTriangle(m + TSIZ / 2, y, TSIZ / 2, "e") + setcolor(hue) + EraseArea(LEFT, m + h - FOOTER, w, FOOTER + m) + CenterString(LEFT + (w - LEFT + m)/2, m + h + m/2 - FOOTER/2, hue) + } + } + e := Event(win) + y := &y + } + return +end + +procedure setcolor(hue) + local i, j, ij, prefix + static prev + + every i := 1 to *sats do + every j := 1 to *lgts do { + ij := i || j + prefix := lgts[j] || " " || sats[i] || " " + if not Color(cwin, \colrs[ij], prefix || hue) then { + # no mutable color was allocated; + # free old static color, preserving grays (used for decoration) + # also preserving labeling colors ("medium vivid") + if \prev ~== "black" & \prev ~== "gray" & \prev ~== "white" then + FreeColor(cwin, ("medium vivid " ~== prefix) || \prev) + Fg(cwin, prefix || hue) + FillRectangle ! fillargs[ij] + } + } + + prev := hue + return +end + +procedure inithues() + local i, y1, y2, dy, win + + huelist := [ + "red", "orange", "red-yellow", "reddish yellow", + "yellow", "greenish yellow", "yellow-green", "yellowish green", + "green", "cyanish green", "cyan-green", "greenish cyan", + "cyan", "bluish cyan", "blue-cyan", "cyanish blue", + "blue", "blue-purple", "purple", "purple-magenta", + "magenta", "reddish magenta", "magenta-red", "magentaish red", + "black", "gray", "white", + "brown", "violet", "pink" + ] + dy := real(h + HGAP) / *huelist + win := Clone(&window) + every i := 1 to *huelist do { + y1 := integer(dy * (i - 1)) + y2 := integer(dy * i) + Fg(win, huelist[i]) + FillRectangle(win, m + TSIZ + 1, m + y1, HUEW - 1, y2 - y1 - HGAP) + } + Uncouple(win) + return +end diff --git a/ipl/gprogs/colrname.icn b/ipl/gprogs/colrname.icn new file mode 100644 index 0000000..2ff4259 --- /dev/null +++ b/ipl/gprogs/colrname.icn @@ -0,0 +1,125 @@ +############################################################################ +# +# File: colrname.icn +# +# Subject: Program to browse color names +# +# Author: Clinton L. Jeffery +# +# Date: August 3, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.1 +# +# Extension to output color specification added by Ralph E. Griswold +# +############################################################################ +# +# An X color name browser. +# +# Click on a colorname to change the window's background color. +# Not very interesting on a monochrome server. +# +############################################################################ +# +# Requires: Version 9 graphics with mutable colors and X. +# +############################################################################ +# +# Links: sort, wopen +# +############################################################################ + +link sort +link wopen + +global w, L, startcols, rows, theBackGround + +procedure drawit() + local curcol, i, maxcol + curcol := 1 + i := 0 + startcols := [1] + maxcol := 0 + every name := !L do { + maxcol <:= *name + GotoRC(i % rows + 1,curcol) + writes(&window,name) + i +:= 1 + if (i>0) & (i % rows = 0) then { + curcol +:= maxcol + 2 + maxcol := 0 + put(startcols,curcol) + } + } +end + + +procedure doevents() + local e, varcol, lastvarcol, lastrow + repeat { + Active() + while Pending()[1] do { + e := Event() + case e of { + "o": write(ColorValue(\name)) + "q"|"\e": exit(0) + &lpress|&mpress|&rpress|&ldrag|&mdrag|&rdrag: { + varcol := 0 + every &col >= !startcols do varcol +:= 1 + if varcol === lastvarcol & &row===lastrow then next + lastvarcol := varcol + lastrow := &row + name := L[(varcol-1)*rows+&row] + Color(theBackGround,name) + WAttrib("label=Color Names: " || name) + } + } + } + } +end + +procedure main(av) + local filename, f, i, t, max, line, t2, r, g, b, rgb + + filename := av[1] | "/usr/lib/X11/rgb.txt" + WOpen("label=Color Names","x","cursor=on","lines=50","columns=175") | + stop("no window") + rows := WAttrib("lines") + f := open(filename) | stop("no rgb.txt") + + theBackGround := NewColor("white") + Bg(theBackGround) + EraseArea() + + i := 1 + t := set() + t2 := table() # skip redundant colors by storing their rgb + max := 0 + every line := !f do { + line ? { + tab(upto(&digits)) + r := tab(many(&digits)) + tab(upto(&digits)) + g := tab(many(&digits)) + tab(upto(&digits)) + b := tab(many(&digits)) + rgb := ishift(r,16)+ishift(g,8)+b + name := (tab(upto(&letters)) & tab(0)) + if /t2[rgb] := name then { + insert(t,name) + max <:= *name + i +:= 1 + } + } + } + L := isort(t) + + drawit() + doevents() +end diff --git a/ipl/gprogs/colrpick.icn b/ipl/gprogs/colrpick.icn new file mode 100644 index 0000000..a8c90f2 --- /dev/null +++ b/ipl/gprogs/colrpick.icn @@ -0,0 +1,70 @@ +############################################################################ +# +# File: colrpick.icn +# +# Subject: Program to pick RGB or HLS colors +# +# Author: Gregg M. Townsend +# +# Date: February 27, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# colrpick provides a command-level interface to the ColorDialog +# procedure. The ColorValue() of the selected color is written to +# standard output when the Okay button is pressed. If the Cancel +# button is pressed, colorpick exits with an error by calling stop(). +# +# A default color can be specified by one or more command arguments, +# for example "colrpick deep green". +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, vsetup +# +############################################################################ + +link graphics +link vsetup + +procedure main(args) + local dflt + + Window ! put(ui_atts(), "canvas=hidden", args) + ui() # just to get standard VIB font + + if *args > 0 then { + dflt := "" + every dflt ||:= " " || !args + if not ColorValue(dflt) then { + write(&errout, " illegal default color: ", dflt) + dflt := &null + } + } + + case ColorDialog(, dflt) of { + "Okay": write(dialog_value) + "Cancel": stop() + } +end + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=340,320", "bg=pale gray"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,340,320:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/concen.icn b/ipl/gprogs/concen.icn new file mode 100644 index 0000000..b00f9f4 --- /dev/null +++ b/ipl/gprogs/concen.icn @@ -0,0 +1,243 @@ +############################################################################ +# +# File: concen.icn +# +# Subject: Program to play solitaire card game Concentration +# +# Author: Gregg M. Townsend +# +# Date: December 4, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# usage: concen [winoptions] [ncards] +# +# Concentration, as presented here, is a simple solitaire game. +# When the program starts, there are 52 playing cards, face down. +# They may be turned over by clicking on them with the mouse. Only +# two cards may be face up at a time; if they are the same rank +# (e.g. two sevens), they are removed. The object is to clear the +# table. +# +# (For an interesting discussion of two-person Concentration, see +# Ian Stewart's "Mathematical Recreations" column in the October, +# 1991, edition of Scientific American, entitled "Concentration: +# A Winning Strategy".) +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: drawcard, options, optwindw, random, graphics +# +############################################################################ + +link drawcard +link options +link optwindw +link random +link graphics + +global deck # full deck of cards +global nleft # number of cards left +global nup # number of cards face up +global uprank # rank of upturned cards, if all same + +global ncols, nrows # number of columns and rows +global cardw, cardh # card width and height +global margin, gap # outside margin, gap between cards +global mono # GC for pattern, iff mono screen + +global cd # card record, indexed by position +record cdrec( + label, # member of &letters as per Icon book + status) # status flag +global VACANT, DOWN, UP # status flag values + +# main program. + +procedure main(args) + local i, j, e + + initialize(args) + newgame() + while e := Event() do { + if e === QuitEvents() then + break + if e === (&lrelease | &mrelease | &rrelease) then { + i := (&y - margin + gap/2) / (cardh + gap) + j := (&x - margin + gap/2) / (cardw + gap) + click(i, j) + } + } +end + +# initialize(args) -- process options, initialize globals, open window + +procedure initialize(args) + local opts, ncards + + cardw := 80 + cardh := 124 + VACANT := 0 + DOWN := 1 + UP := 2 + + opts := options(args, winoptions()) # get command options + + ncards := integer(args[1]) | 52 # get size of deck + ncards -:= ncards % 2 # ensure even + ncards <:= 2 # ensure at least 2 cards + ncards >:= 52 # ensure at most 52 cards + deck := + ("aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" ? move(ncards)) + + if ncards <= 10 then + nrows := 2 + else if ncards <= 21 then + nrows := 3 + else if ncards <= 36 then + nrows := 4 + else + nrows := 5 + ncols := (ncards + nrows - 1) / nrows + + /opts["M"] := 20 + margin := opts["M"] + gap := margin / 2 + /opts["W"] := ncols * cardw + (ncols - 1) * gap + /opts["H"] := nrows * cardh + (nrows - 1) * gap + /opts["B"] := "deep moderate green" + &window := optwindow(opts) + if WAttrib("depth") = 1 then { + mono := Clone(&window, "fg=white", "bg=black", "fillstyle=textured") + Pattern(mono, "4,2,8,2,8") + FillRectangle(mono, 0, 0, 2 * margin + opts["W"], 2 * margin + opts["H"]) + } + randomize() + return +end + +# newgame() -- lay out cards, face down, for a new game + +procedure newgame() + local i, j, s + + nleft := *deck + nup := 0 + cd := [] + every put(cd, cdrec(!deck, DOWN)) + every i := *cd to 2 by -1 do + cd[?i] :=: cd[i] + + every i := 0 to nrows-1 do + every j := 0 to ncols-1 do + if cardno(i, j) then + setcard(i, j, "-") + + return +end + +# click(i, j) -- process a click on the card in row i, column j + +procedure click(i, j) + local c + + case nup of { # action depends on the number of cards already face up + + 0: { + # no cards are face up. turn this one up. + c := cd[cardno(i, j)] | fail + if c.status = DOWN then { + setcard(i, j, c.label) + c.status := UP + nup := 1 + uprank := crank(c.label) + } + } + + 1: { + # one is face up. it might be the one clicked. + c := cd[cardno(i, j)] | fail + if c.status = UP then { + setcard(i, j, "-") + c.status := DOWN + nup := 0 + } + else if c.status = DOWN then { + setcard(i, j, c.label) + c.status := UP + nup := 2 + if uprank ~= crank(c.label) then + uprank := &null + } + } + + 2: { + # two are face up. it doesn't matter what card was clicked. + # remove the two up-cards if they match, or turn back over if not. + every i := 0 to nrows-1 do + every j := 0 to ncols-1 do + if c := cd[cardno(i, j)] then + if c.status = UP then { + if \uprank then { + setcard(i, j, &null) + c.status := VACANT + nleft -:= 1 + } + else { + setcard(i, j, "-") + c.status := DOWN + } + nup -:= 1 + } + # if no cards are left, the game is won. + # show all cards face up as a reward. + if nleft = 0 then + every i := 0 to nrows-1 do + every j := 0 to ncols-1 do + if c := cd[cardno(i, j)] then { + setcard(i, j, c.label) + c.status := UP + nup +:= 1 + } + } + default: + # presumably there are 52 cards face up after a win. + # start a new game with this new click. + newgame() + } + return +end + +# setcard(i, j, c) -- redraw card c at location (i,j), or background if /c + +procedure setcard(i, j, c) + local x, y + x := margin + j * (cardw + gap) + y := margin + i * (cardh + gap) + drawcard(x, y, \c) | + FillRectangle(\mono, x, y, cardw, cardh) | + EraseArea(x, y, cardw, cardh) + return +end + +# cardno(i, j) -- return index (1 to 52) if location is valid + +procedure cardno(i, j) + return (0 <= i) & (0 <= j < ncols) & *deck >= (ncols * i + j + 1) +end + +# crank(label) -- return rank (1 to 13) of card with given label + +procedure crank(label) + static fulldeck + initial fulldeck := string(&letters) + return fulldeck ? find(label) % 13 +end diff --git a/ipl/gprogs/cquilts.icn b/ipl/gprogs/cquilts.icn new file mode 100644 index 0000000..633315f --- /dev/null +++ b/ipl/gprogs/cquilts.icn @@ -0,0 +1,239 @@ +############################################################################ +# +# File: cquilts.icn +# +# Subject: Program to create "chaotic square quilts" +# +# Author: Ralph E. Griswold +# +# Date: March 14, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program creates square quilting patterns as described in +# "Symmetry in Chaos", Michael Field and Martin Golubitsky, Oxford +# University Press, 1992. +# +# Instead of plotting an image, the values are computed and saved +# in "numerical carpets" for off-line plotting. +# +# The following options are supported: +# +# -i i Save carpet files every i iterations; default 100000 +# +# -p s Prefix for carpet file names, default q_ +# +# -t i Terminate execution after i iterations; default no limit +# +# Warning: This program takes a long time to go through enough iterations +# to produce nice results. +# +# Note: This is an unfinished work, supplied for interest only. +# +# There are several sections of parameter values below. All but one +# is commented out. Change this to get other patterns. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: matrix, options, writecpt +# +############################################################################ + +link matrix +link options +link writecpt + +global pi_2 +global pi_4 +global pi_6 + +$define Size 200 + +procedure main(args) + local x, y, xnew, ynew, lambda, alpha, beta, gamma, omega, ma, shift + local mcount, sx, sy, xp, yp, max, min, i + local count, prefix, iter, opts, interval, limit + + pi_2 := 2 * &pi + pi_4 := 4 * &pi + pi_6 := 6 * &pi + + iter := 0 + count := -1 + + opts := options(args, "i+p:t+") + + interval := \opts["i"] | 100000 + prefix := \opts["p"] | "q_" + limit := \opts["t"] + + xnew := x := 0.1 + ynew := y := 0.334 + +# Sugar and Spice + +# lambda := -0.59 +# alpha := 0.2 +# beta := 0.1 +# gamma := -0.27 +# omega := 0.0 +# ma := 0.0 +# shift := 0.5 + +# Emerald Mosaic + +# lambda := -0.59 +# alpha := 0.2 +# beta := 0.1 +# gamma := -0.33 +# omega := 0.0 +# ma := 2.0 +# shift := 0.0 + +# Sicilian Tile + +# lambda := -0.2 +# alpha := -0.1 +# beta := 0.1 +# gamma := -0.25 +# omega := 0.0 +# ma := 0.0 +# shift := 0.0 + +# Roses + +# lambda := 0.25 +# alpha := -0.3 +# beta := 0.2 +# gamma := 0.3 +# omega := 0.0 +# ma := 1.0 +# shift := 0.0 + +# Wagon Wheels + +# lambda := -0.28 +# alpha := 0.25 +# beta := 0.05 +# gamma := -0.24 +# omega := 0.0 +# shift := 0.0 +# ma := -1.0 + +# Victorian Tiles + +# lambda := -0.12 +# alpha := -0.36 +# beta := 0.18 +# gamma := -0.14 +# omega := 0.0 +# shift := 0.5 +# ma := 1.0 + +# Mosque + +# lambda := 0.1 +# alpha := 0.2 +# beta := 0.1 +# gamma := 0.39 +# omega := 0.0 +# shift := 0.0 +# ma := -1.0 + +# Red Tiles + +# lambda := -0.589 +# alpha := 0.2 +# beta := 0.04 +# gamma := -0.2 +# omega := 0.0 +# shift := 0.5 +# ma := 0.0 + +# Cathedral Attractor + +# lambda := -0.28 +# alpha := 0.08 +# beta := 0.45 +# gamma := -0.05 +# omega := 0.0 +# shift := 0.5 +# ma := 0.0 + +# Gyroscopes + +# lambda := -0.59 +# alpha := 0.2 +# beta := 0.2 +# gamma := 0.3 +# omega := 0.0 +# shift := 0.0 +# ma := 2.0 + +# Cats Eyes + +# lambda := -0.28 +# alpha := 0.25 +# beta := 0.05 +# gamma := -0.24 +# omega := 0.0 +# shift := 0.5 +# ma := -1.0 + +# Flowers with Ribbons + + lambda := -0.11 + alpha := -0.26 + beta := 0.19 + gamma := -0.059 + omega := 0.07 + shift := 0.5 + ma := 2.0 + + mcount := create_matrix(Size, Size, 0) + + repeat { + + # iterate + sx := sin(pi_2 * x) + sy := sin(pi_2 * y) + xnew := (lambda + alpha * cos(pi_2 * y)) * sx - omega * sy + beta * + sin(pi_4 * x) + gamma * sin(pi_6 * x) * cos(pi_4 * y) + ma * + x + shift + ynew := (lambda + alpha * cos(pi_2 * x)) * sy + omega * sx + beta * + sin(pi_4 * y) + gamma * sin(pi_6 * y) * cos(pi_4 * x) + ma * + y + shift + if xnew > 1.0 then xnew -:= integer(xnew) + else if xnew < 0.0 then xnew +:= integer(-xnew) + 1 + if ynew > 1.0 then ynew -:= integer(ynew) + else if ynew < 0.0 then ynew +:= integer(-ynew) + 1 + x := xnew + y := ynew + + xp := integer(Size * x) + yp := integer(Size * y) + mcount[xp + 1, yp + 1] +:= 1 + iter +:= 1 + if iter % \interval = 0 then { + max := 0 + min := 2 ^ 31 + every i := mcount[1 to Size, 1 to Size] do { + max <:= i + min >:= i + } + if min < 0 then min := 0 + write_cpt(prefix || right(count +:= 1, 3, "0") || ".cpt", + mcount, min, max) + } + if iter >= \limit then exit() + } + +end diff --git a/ipl/gprogs/cw.icn b/ipl/gprogs/cw.icn new file mode 100644 index 0000000..34cd851 --- /dev/null +++ b/ipl/gprogs/cw.icn @@ -0,0 +1,48 @@ +############################################################################ +# +# File: cw.icn +# +# Subject: Program to manipulate color ways +# +# Author: Ralph E. Griswold +# +# Date: August 19, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# See colorway.icn for documentation +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: colorway +# +############################################################################ + +link colorway + +procedure main() + + cw_init() + + cw := colorway(table()) # initial color way + cw.table["white"] := "white" + cw.table["black"] := "black" + cw_file := "" + win_cw() + expose(cw_interface) + + repeat { + if \cw_active then edit_cw() + else ProcessEvent(cw_root, , shortcuts) + } + + +end diff --git a/ipl/gprogs/dd2draft.icn b/ipl/gprogs/dd2draft.icn new file mode 100644 index 0000000..f98c247 --- /dev/null +++ b/ipl/gprogs/dd2draft.icn @@ -0,0 +1,111 @@ +############################################################################ +# +# File: dd2draft.icn +# +# Subject: Program to create draft information from drawdown +# +# Author: Ralph E. Griswold +# +# Date: November 16, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads a drawdown in terms of rows of zeros and ones from +# standard input and outputs draft information in textual form. +# +# It also accepts BLPs as input. +# +############################################################################ +# +# Links: patutils. patxform +# +############################################################################ + +link patutils +link patxform + +record analysis(rows, sequence, patterns) + +procedure main() + local threading, treadling, rows, columns, pattern, i + local symbols, symbol, tieup, line + + line := read() | stop("empty file") + + if upto("#", line) then rows := pat2rows(line) + else { + rows := [line] + while put(rows, read()) # read in row pattern + } + + write("Drawdown:") + write() + every write(!rows) + write() + + treadling := analyze(rows) + write("Treadling:") + write() + every writes(!treadling.sequence, ", ") + write() + write() + + columns := protate(rows) # rotate 90 degrees + + threading := analyze(columns) + write("Threading:") + write() + every writes(!threading.sequence, ", ") + write() + write() + + # Now do the tie-up. + + symbols := table("") + + every pattern := !treadling.patterns do { + symbol := treadling.rows[pattern] + symbols[symbol] := repl("0", *threading.rows) + pattern ? { + every i := upto('1') do + symbols[symbol][threading.sequence[i]] := "1" + } + } + + symbols := sort(symbols, 3) + tieup := [] + + while get(symbols) do + put(tieup, get(symbols)) + + write("Tie-up:") + write() + every write(!tieup) + +end + +procedure analyze(drawdown) + local sequence, rows, row, count, patterns + + sequence := [] + patterns := [] + + rows := table() + + count := 0 + + every row := !drawdown do { + if /rows[row] then { + rows[row] := count +:= 1 + put(patterns, row) + } + put(sequence, rows[row]) + } + + return analysis(rows, sequence, patterns) + +end diff --git a/ipl/gprogs/dd2res.icn b/ipl/gprogs/dd2res.icn new file mode 100644 index 0000000..785c8b6 --- /dev/null +++ b/ipl/gprogs/dd2res.icn @@ -0,0 +1,39 @@ +############################################################################ +# +# File: dd2res.icn +# +# Subject: Program to compute loom resources needed from drawdown +# +# Author: Ralph E. Griswold +# +# Date: July 8, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads a pattern in row or BLP format. +# +# The number of shafts and treadles required is written to standard +# output. +# +############################################################################ +# +# Links: pattread, patutils, patxform +# +############################################################################ + +link pattread +link patutils +link patxform + +procedure main() + local rows, row + + rows := pattread() + + write(*set(protate(rows)), "x", *set(rows)) + +end diff --git a/ipl/gprogs/dd2unit.icn b/ipl/gprogs/dd2unit.icn new file mode 100644 index 0000000..09c2d98 --- /dev/null +++ b/ipl/gprogs/dd2unit.icn @@ -0,0 +1,87 @@ +############################################################################ +# +# File: dd2unit.icn +# +# Subject: Program to get dimensions of unit motif of pattern +# +# Author: Ralph E. Griswold +# +# Date: June 12, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# The following command line option is supported: +# +# -p assume partial repeats may occur at edges of pattern; +# default complete repeats +# +############################################################################ +# +# Links: options, patutils, seqops +# +############################################################################ + +link options +link patutils +link seqops + +global switch + +procedure main(args) + local rows, opts + + opts := options(args, "p") + switch := opts["p"] + + rows := unit(pat2rows(read())) + write(*rows[1], "x", *rows) + +end + +procedure rot90(rows) # rotate pattern 90 degrees clockwise + local columns, i, j + + columns := list(*rows[1], "") + + every i := 1 to *rows do + every j := 1 to *columns do + columns[j] ||:= rows[i][j] + + return columns + +end + +procedure unit(grid) + + grid := grepeat(grid) + + grid := grepeat(rot90(grid)) + + return rot90(grid) + +end + +procedure grepeat(grid) #: reduce grid to smallest repeat + local periods, i, width + + grid := copy(grid) + + periods := [] + + width := *grid[1] + + every i := 1 to *grid do + put(periods, speriod(str2lst(grid[i]), switch) | width) + + width >:= lcml ! periods + + every i := 1 to *grid do + grid[i] := left(grid[i], width) + + return grid + +end diff --git a/ipl/gprogs/dd2wif.icn b/ipl/gprogs/dd2wif.icn new file mode 100644 index 0000000..51a7c5f --- /dev/null +++ b/ipl/gprogs/dd2wif.icn @@ -0,0 +1,182 @@ +############################################################################ +# +# File: dd2wif.icn +# +# Subject: Program to produce a WIF from drawdown +# +# Author: Ralph E. Griswold +# +# Date: July 4, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads BLPs that represent drawdowns. The names of BLP +# files are given on the command line. WIF files are output. +# +# The following option is supported: +# +# -w make Web page; default don't +# +# If Web pages are being produced, the extension "html" is used; otherwise +# "wif". +# +############################################################################ +# +# Links: basename, options, pattread, patutils +# +############################################################################ + +link basename +link options +link pattread +link patutils + +procedure main(args) + local rows, cols, treadling, threading, count, tieup, line, opts, lt, ext + local shafts, treadles, i, tie_line, row, treadle, draft, title, web + local name, input, output + + opts := options(args, "w") + + title := \opts["t"] | "example" + web := \opts["w"] + + if \web then { # make Web page + lt := "<BR>" + ext := ".html" + } + else ext := ".wif" + + every name := !args do { + input := open(name) | stop("Cannot open ", name) + rows := pattread(input) + close(input) + output := open(basename(name, ".blp") || ext, "w") | + stop("Cannot open file for writing.") + write(output, "<HTML>") + write(output, "<BODY>") + cols := rot(rows) # rotate to get columns + treadles := examine(rows) # get treadles + shafts := examine(cols) # get shafts + treadling := [] # construct treadling sequence + every put(treadling, treadles[!rows]) + threading := [] # construct threading sequence + every put(threading, shafts[!cols]) + tieup := table() + every row := key(treadles) do { # get unique rows + treadle := treadles[row] # assigned treadle number + tie_line := repl("0", *shafts) # blank tie-up line + every i := 1 to *row do # go through row + if row[i] == "1" then # if warp on top + tie_line[threading[i]] := "1" # mark shaft position + tieup[treadle] := tie_line # add line to tie-up + } + write(output, "[WIF]", lt) + write(output, "Version=1.1", lt) + write(output, "Date=" || &dateline, lt) + write(output, "Developers=ralph@cs.arizona.edu", lt) + write(output, "Source Program=dd2wif.icn", lt) + write(output, "[CONTENTS]", lt) + write(output, "Color Palette=yes", lt) + write(output, "Text=yes", lt) + write(output, "Weaving=yes", lt) + write(output, "Tieup=yes", lt) + write(output, "Color Table=yes", lt) + write(output, "Threading=yes", lt) + write(output, "Treadling=yes", lt) + write(output, "Warp colors=yes", lt) + write(output, "Weft colors=yes", lt) + write(output, "Warp=yes", lt) + write(output, "Weft=yes", lt) + write(output, "[COLOR PALETTE]", lt) + write(output, "Entries=2", lt) + write(output, "Form=RGB", lt) + write(output, "Range=0," || 2 ^ 16 - 1, lt) + write(output, "[TEXT]", lt) + write(output, "Title=", basename(name, ".blp"), lt) + write(output, "Author=Ralph E. Griswold", lt) + write(output, "Address=5302 E. 4th St., Tucson, AZ 85711-2304", lt) + write(output, "EMail=ralph@cs.arizona.edu", lt) + write(output, "Telephone=520-881-1470", lt) + write(output, "FAX=520-325-3948", lt) + write(output, "[WEAVING]", lt) + write(output, "Shafts=", *shafts, lt) + write(output, "Treadles=", *treadles, lt) + write(output, "Rising shed=yes", lt) + write(output, "[WARP]", lt) + write(output, "Threads=", *threading, lt) + write(output, "Units=Decipoints", lt) + write(output, "Thickness=10", lt) + write(output, "Color=1", lt) + write(output, "[WEFT]", lt) + write(output, "Threads=", *treadling, lt) + write(output, "Units=Decipoints", lt) + write(output, "Thickness=10", lt) + write(output, "Color=2", lt) + write(output, "[WARP THICKNESS]", lt) + write(output, "[WEFT THICKNESS]", lt) + write(output, "[COLOR TABLE]", lt) + write(output, "1=0,0,0", lt) + write(output, "2=65535,65535,65535", lt) + write(output, "[THREADING]", lt) + every i := 1 to *threading do + write(output, i, "=", threading[i], lt) + write(output, "[TREADLING]", lt) + every i := 1 to *treadling do + write(output, i, "=", treadling[i], lt) + write(output, "[TIEUP]", lt) + every i := 1 to *tieup do + write(output, i, "=", tromp(tieup[i]), lt) + if \web then { + write(output, "</BODY>") + write(output, "</HTML>") + } + close(output) + } + +end + +procedure tromp(treadle) + local result + + result := "" + + treadle ? { + every result ||:= upto("1") || "," + } + + return result[1:-1] + +end + +procedure examine(array) + local count, lines, line + + lines := table() # table to be keyed by line patterns + count := 0 + + every line := !array do # process lines + /lines[line] := (count +:= 1) # if new line, insert with new number + + return lines + +end + +procedure rot(rows) + local cols, row, grid, i + + cols := list(*rows[1], "") + + every row := !rows do { + i := 0 + every grid := !row do + cols[i +:= 1] := grid || cols[i] + } + + return cols + +end diff --git a/ipl/gprogs/ddextend.icn b/ipl/gprogs/ddextend.icn new file mode 100644 index 0000000..61f590e --- /dev/null +++ b/ipl/gprogs/ddextend.icn @@ -0,0 +1,80 @@ +############################################################################ +# +# File: ddextend.icn +# +# Subject: Program to extend pattern to a minimum size +# +# Author: Ralph E. Griswold +# +# Date: June 11, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads a drawdown from standard input in the form of +# rows of zeros and ones, in which ones indicate places where the +# warp thread is on top and zeros where the weft thread is on top. +# It also accepts a BLP as input. +# +# At present, the minimum size is 16, built in. This should be changed +# to a value that could be specified as an option. +# +# It outputs a BLP. +# +############################################################################ +# +# Links: patutils, patxform +# +############################################################################ + +link patutils +link patxform + +$define Minimum 16 + +procedure main() + local line, rows, q, r, new_rows + + rows := [] + + line := read() | stop("empty file") + + if upto("#", line) then rows := pat2rows(line) + else { + rows := [line] + while put(rows, read()) # read in row pattern + } + + while put(rows, read()) + + # extend width if necessary + + if *rows[1] < Minimum then { + q := Minimum / *rows[1] + r := Minimum % *rows[1] + if r ~= 0 then q +:= 1 # extension factor + new_rows := copy(rows) + every 2 to q do + new_rows := pcaten(new_rows, rows, "h") + rows := new_rows + } + + # extend height if necessary + + if *rows < Minimum then { + q := Minimum / *rows + r := Minimum % *rows + if r ~= 0 then q +:= 1 # extension factor + new_rows := copy(rows) + every 2 to q do + new_rows := pcaten(new_rows, rows, "v") + rows := new_rows + } + + write(rows2pat(rows)) + + +end diff --git a/ipl/gprogs/design1.icn b/ipl/gprogs/design1.icn new file mode 100644 index 0000000..825c5f8 --- /dev/null +++ b/ipl/gprogs/design1.icn @@ -0,0 +1,70 @@ +############################################################################ +# +# File: design1.icn +# +# Subject: Program to draw spokes design +# +# Author: Ralph E. Griswold +# +# Date: February 17, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is just an example of an interesting graphic design. It can +# easily be modified to produce other designs. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(argl) + local i, j, k, angle, incr, xpoint, ypoint, size, radius, xc, yc + + i := integer(argl[1]) | 20 + + size := 300 + radius := size / 4 + xc := yc := size / 2 + + WOpen("label=design", "width=" || size, "height=" || size) | + stop("*** cannot open window") + + angle := 0.0 + incr := 2 * &pi / i + + every j := 1 to i do { + spokes(xc + radius * cos(angle), yc + radius * sin(angle), + radius, i, angle) + angle +:= incr + } + + Event() + +end + +procedure spokes(x, y, r, i, angle) + local incr, j + + incr := 2 * &pi / i + + every j := 1 to i do { + DrawLine(x, y, x + r * cos(angle), y + r * sin(angle)) + angle +:= incr + } + + return + +end + diff --git a/ipl/gprogs/design2.icn b/ipl/gprogs/design2.icn new file mode 100644 index 0000000..8ee72ac --- /dev/null +++ b/ipl/gprogs/design2.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: design2.icn +# +# Subject: Program to draw circular design +# +# Author: Ralph E. Griswold +# +# Date: February 17, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws a design in which i points around a circle are +# all connected to each other. The number of points is given as +# a command-line argument (default 20). Values larger than 30 produce +# results that are too dense to be interesting. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: gobject, joinpair, wopen +# +############################################################################ + +link gobject +link joinpair +link wopen + +procedure main(argl) + local i, j, k, angle, incr, size, radius, xc, yc, points + + i := integer(argl[1]) | 20 + + size := 300 + radius := size / 2 + xc := yc := size / 2 + + WOpen("label=mandala", "width=" || size, "height=" || size) | + stop("*** cannot open window") + + points := list(i) + + angle := 0.0 + incr := 2 * &pi / i + + every j := 1 to i do { + points[j] := Point(xc + radius * cos(angle), yc + radius * sin(angle)) + angle +:= incr + } + + joinpair(points, points) + + Event() + +end diff --git a/ipl/gprogs/design3.icn b/ipl/gprogs/design3.icn new file mode 100644 index 0000000..3e31dbc --- /dev/null +++ b/ipl/gprogs/design3.icn @@ -0,0 +1,63 @@ +############################################################################ +# +# File: design3.icn +# +# Subject: Program to draw square design +# +# Author: Ralph E. Griswold +# +# Date: February 17, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws a design in which i points around a square are +# all connected to each other. The number of points along a side +# (default 10) and the distance between them (default 40) are given as +# command-line arguments. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: gobject, joinpair, wopen +# +############################################################################ + +link gobject +link joinpair +link wopen + +procedure main(argl) + local i, j, k, d, extent, points, x, y + + i := integer(argl[1]) | 10 + d := integer(argl[2]) | 40 + + extent := i * d + + WOpen("label=mandala", "width=" || extent, "height=" || extent) | + stop("*** cannot open window") + + points := [] + + every x := 0 to extent by d do { # x direction, with corners + put(points, Point(x, 0)) # top + put(points, Point(x, extent)) # bottom + } + + every y := d to extent - d by d do { # y direction, without corners + put(points, Point(0, y)) # left side + put(points, Point(extent, y)) # right side + } + + joinpair(points, points) + + Event() + +end diff --git a/ipl/gprogs/dlgvu.icn b/ipl/gprogs/dlgvu.icn new file mode 100644 index 0000000..63a933d --- /dev/null +++ b/ipl/gprogs/dlgvu.icn @@ -0,0 +1,1900 @@ +############################################################################ +# +# File: dlgvu.icn +# +# Subject: Program to display USGS DLG map files +# +# Authors: Gregg M. Townsend and William S. Evans +# +# Date: October 2, 2005 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Contributor: Frank Glandorf +# +############################################################################ +# +# Dlgvu displays and prints USGS digital map data. +# +# usage: dlgvu [options] file... +# +# Each file argument is one of: +# a directory containing DLG files in SDTS format +# a ZIP format archive of such files (requires "unzip" utility) +# a text file containing coordinates of paths and features +# +# All interaction is via mouse actions and keyboard shortcuts. +# The display window may be resized as desired. +# +############################################################################ +# +# Command options: +# +# -c display coverage map only, without loading data +# -d print some debugging data +# -n no display; just report statistics, and then quit +# (this still requires X-Windows, unfortunately) +# -p use slower, more precise coordinate conversion +# -q quiet mode: no commentary to stdout +# -t load only maps traversed by paths, ignoring others +# -o logfile specify output log to use instead of standard output +# +# -l abcd display only layers a, b, c, d +# -x abcd exclude layers a, b, c, d +# +# For -l and -x, the following layer codes are used. +# (USGS equivalents are given in parentheses.) +# +# b boundaries (BD: boundaries) +# c contour lines (HP: hypsography) +# d sand, gravel, lava (NV: nonvegetative features) +# f feature labels read from text files +# g GPS paths read from text files +# l land sections (PL: public lands) +# m markers (SM: survey markers) +# n file names +# o other unknown layers from non-DLG data +# r roads (RD: roads) +# s structures (MS: manmade structures) +# t train tracks (RR: railroads) +# u utilities (MT: miscellaneous transportation) +# v vegetation (SC: surface cover) +# w water (HY: hydrology) +# +# Additionally, the standard Window() options are accepted; +# in particular, "-F fgcolor" sets the color used for drawing +# unrecognized ("other") layers, as from USGS "National Atlas" +# digital files, and "-G 800x500" sets the initial window size. +# +# Typical usage is simply +# dlgvu dir1 [dir2 ...] +# to display one or more adjacent maps. The -x option can speed +# things up by excluding unwanted layers; the contour layer is +# especially slow. +# +# A ZIP archive can replace a directory name if Icon can open +# the unzip program via a pipe. For example: +# dlgvu woodside.zip palo_alto.zip +# +############################################################################ +# +# Mouse actions: +# +# To zoom to a particular region, sweep out the region using the +# left mouse button. To cancel a sweep, reduce its width or height +# to fewer than ten pixels. +# +# If nothing appears to be happening after zooming in, the program +# is probably drawing offscreen. It's not smart about that. Be +# patient, and it will soon display the visible region. +# +# To display the latitude / longitude of a location, and a scale bar, +# hold down the right mouse button. +# +# To record a labeled feature, shift-click the left mouse button. +# Enter a name in the pop-up dialog box. The location and name are +# written to the log file and added to the feature layer of the map. +# +# To record an anonymous location to the log file, shift-click with +# the right mouse button instead. No dialog box appears. A sequence +# of anonymous locations can be read as a path by a subsequent +# program run. +# +############################################################################ +# +# Keyboard actions: +# +# + or = zoom in +# - or _ zoom out +# 0 or Home zoom to initial view +# arrow keys pan the display (hold Shift key for smaller pan) +# +# b, c, d, etc. toggle display of specified layer +# a display all loaded layers including n (file names) +# x display no layers (just an empty window) +# +# Esc stop drawing (any unrecognized key does this) +# space or Enter redraw screen (e.g. after inadvertent interrupt) +# q quit +# +# p or PrntScrn print visible portion to PostScript file +# +# The file produced by PrntScrn is an Encapsulated PostScript file +# suitable either for direct printing ("lpr file.ps") or for import +# into another document. +# +############################################################################ +# +# Input files: +# +# In directories and archives, only files with names ending in .ddf +# or .DDF are read; others are ignored. These files must be in SDTS +# (Spatial Data Transfer Standard) format, which is used by the USGS +# for all new DLG files. +# +# Text files supply coordinates for features or paths. GPS receivers +# are one possible source for such data. A text file can supply +# paths, features, or both. +# +# Paths are specified by sequences of lines that end with two decimal +# values. The values are interpreted as latitude and longitude, in +# that order. An interruption in the sequence (such as a blank line) +# indicates a break between paths. +# +# Features, or waypoints, are given by lines that *begin* with two +# decimal values. The rest of the line is taken as a label, which +# must not be empty and must not end with two decimal values. +# +# Any other line in a text file breaks a path sequence but is +# otherwise ignored. +# +############################################################################ +# +# About DLG files: +# +# Dlgvu was written to display digitized topographic maps produced +# by the United States Geological Survey (USGS). The current file +# format is based on the Spatial Data Transfer Standard (SDTS). +# Some older files are available in other formats (including +# "standard" and "optional") not supported by this program. +# +# DLG files are available free from the USGS at this web page: +# http://edc.usgs.gov/doc/edchome/ndcdb/ndcdb.html +# Coverage is incomplete. 24K maps, the most detailed, are available +# for only some areas, and many maps lack some of the data layers. +# +# Each map is represented by a collection of gzipped tar files +# (one for each map layer) that are unpacked for display. Multiple +# files versions may be available, and not all layers are available +# for all maps. +# +# IMPORTANT: Do not blindly unpack all the tar files of a map into +# the same directory; due to the use of duplicate file names in the +# transportation layers, some files will be overwritten. Instead, +# unpack the roads, railroads, and miscellaneous transportation +# layers separately, each time renaming the TR*.DDF files to RD*.DDF, +# RR*.DDF, and MT*.DDF respectively. +# +# Dlgvu has mainly been tested and tuned using "large scale" DLG +# files (1:24000 scale, covering 7.5 minute quadrangles). Other +# scales produce less attractive displays, partly due to different +# encodings: For example, the same residential streets may be encoded +# as "Class 3 Roads" in 24K files but "Class 4 Roads" in 100K files. +# +# Dlgvu does not presume to understand ISO 8211, DDF, STDS, and TVP +# in their full complexity and generality. Undoubtedly it is making +# some unwarranted assumptions based on observed practice. The file +# renaming recommended above is contrary to specification but allows +# a complete map to be stored in a single flat directory. +# +# For more information, and some sample data files, visit: +# http://www.cs.arizona.edu/icon/oddsends/dlgvu/ +# +############################################################################ +# +# Displayed features: +# +# DLG files are rich in detail. Dlgvu displays only some of this +# encoded data. +# +# Put simply, dlgvu understands point and line features but not +# area features. It draws a small square for a structure location, +# or draws the outline of a large building, but it does not color in +# an "urban area" in which individual structures are not plotted. +# It displays the shoreline of a river, and any islands, but does +# not understand enough to color the river area itself blue. +# +# Dlgvu recognizes some line features for special display. For +# example, major roads are drawn with wide lines, and trails are +# drawn with dashed red lines. Lines with unrecognized attributes, +# or no attributes, are drawn in a default style. Point features +# ("school", "windmill", etc.) are not distinguished. +# +# Area features are drawn only in outline. The most obvious of +# these are vegetated areas and urban areas. Land section and +# civil boundaries also delimit area features. +# +# Colors are assigned as follows (with layer codes on the left): +# +# b boundaries gold +# c contour lines tan +# f feature labels black +# g GPS path bold pink over "highlighter" +# l land sections pale red +# m survey markers blue +# n file names green +# o other data brown (can override with -F option) +# r roads, class 1-3 black or dark gray +# r roads, class 4-5 dashed dark gray +# r trails dashed red +# s structures brownish gray +# t railroads rust +# t rapid transit rails dark blue +# u pipelines dashed purple +# u power lines purple +# u airport runways gray +# v vegetation light green +# w water light blue +# x sand, gravel, lava greenish gray +# +# Dlgvu uses a simple rectangular projection that is satisfactory +# for small areas like 24K quadrangles but less suitable for large +# areas such as whole states. +# +############################################################################ +# +# The loading process: +# +# Data is loaded in two phases. A quick preloading phase determines +# the available layers and the geographic areas covered. A status +# line is output for each file. For example: +# +# bcl-r-tu-w N27 15 C66 42a 93a ia/ames-w +# +# The first field shows which layers were found. N27 declares that +# coordinates use the NAD 1927 geodetic datum; N83 for NAD 1983 is +# the other likely value. 15 is the UTM zone number; state maps with +# latitude/longitude data show "LL" here. C66 means that the data +# appears to have been projected using the Clarke 1866 ellipsoid; the +# other likely value is "G80" for the GRS 1980 ellipsoid. Dlgvu uses +# this to infer the datum, because the declared datum value is a less +# reliable indicator. +# +# "42a 93a" gives the coordinates of the southeast corner of the map, +# in degrees North and West, with letters "a" through "h" indicating +# fractions from 0/8 through 7/8. The final field is the file name. +# +# If the layers in a file are inconsistent (for example, in the +# inferred ellipsoid), multiple lines appear with a "*" prefix. +# If display of a file is suppressed by the "-t" option, an X +# prefixes the line. +# +# For text files, a notation such as "3:489+0" replaces layer +# indicators, counting continuous segments, total points, and +# feature labels. Coordinate values are assumed to use the +# WGS 1984 ("W84") datum and ellipsoid. +# +# The display appears during the longer second loading phase. For +# each layer of each input file, bounds are drawn and a progress bar +# changes as data is read. The color of the label indicates the +# layer being loaded. +# +############################################################################ +# +# Tiling multiple maps: +# +# Multiple maps are displayed in proper relation. To quickly see +# how the maps of a set will join, use "dlgvu -c". +# +# Small gaps or overlaps occasionally appear along boundaries when +# maps are tiled; these are symptomatic of inconsistent datums, and +# they reflect the true relationships of the maps to the earth. +# +# Dlgvu loads all necessary data into memory, so there is a very +# real limit to the amount of data that can be displayed. Contour +# lines, especially, take lots of memory, but they can be excluded +# by calling "dlgvu -xc". A 128MB Linux system can typically +# display three to five complex 24K quadrangles simultaneously +# without thrashing. +# +############################################################################ +# +# Known problems: +# +# On Linux, we have seen occasional crashes of the XFree86 server, +# especially under conditions of tight memory and/or extreme zooming. +# +# Colors on printed maps vary somewhat from those seen onscreen, +# depending on the printer. Printed maps do not include the "n" +# (file name) layer. +# +# While data is being loaded from a ZIP file, interrupting with ^Z +# can disrupt the "unzip" pipe and cause the program to crash or to +# display artifacts after resumption. +# +# Some 100K USGS maps come with multiple sets of boundary files, +# leading to file name collisions for which no workaround has been +# found. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: cartog, clipping, ddfread, geodat, graphics, io, mapnav, +# numbers, options, pscript, random, strings, wildcard, zipread +# +############################################################################ + + + +$include "keysyms.icn" + +link cartog +link clipping +link ddfread +link geodat +link graphics +link io +link mapnav +link numbers +link options +link pscript +link random +link strings +link wildcard +link zipread + + + +$define DLG_LAYERS "boclvdwsrtum" # all "real" layers, in loading order + +$define WSIZE "size=1000,1000" # default window size +$define ZOOMF 1.5 # zoom factor + +$define MAXFONT 18 # maximum font size +$define MINFONT 8 # minimum font size +$define MINBOLD 10 # minimum bold font size + +$define MEGABYTE (1024 * 1024) # how many bytes in a megabyte? +$define STRSIZE (1 * MEGABYTE) # default string region size +$define BLKSIZE (16 * MEGABYTE) # default block region size +$define MAXDRAW 4000 # maximum (even) args to avoid error 301 + +$define DEGSCALE 1.0e+6 # divisions per degree for integer scale + +# parameters for displaying progress during loading +$define PINTERVAL 100 # progress interval +$define PSQUARES 8 # number of progress squares +$define PSQSIZE 10 # size of progress squares +$define PSQGAP 2 # size of gap between squares + +# PostScript output parameters +$define PSSCALE 10 # scaling from pixels to PS units +$define PSPT 24 # size of point feature in PS units +$define PSLWI 120 # linewidth scaling factor + + + +record arg ( # command-line argument, excluding options + name, # file or directory name + type, # "dir", "zip", or "txt" + wanted, # if a wanted layer (null if suppressed by -t option) + ltable, # table of layer records, indexed by layer code + pcount # progress bar counter + ) + +record layer ( # one layer in one directory (or zip file) + lcode, # layer code character + arg, # corresponding arg record + files, # list of file names + zone, # UTM zone, or -1 if data is in lat/lon , from XREF file + xscale, yscale, # scaling factors for file values + datum, # stated coordinate datum, from XREF file + ellipsoid, # inferred geodetic ellipsoid + icorners, # map corners in input terms + ocorners, # map corners as projected to lat,lon + px, py, # progress reporting coordinates + wd # width of layer in screen units + ) + +record attrec ( # line drawing attributes: + seq, # drawing sequence + lcode, # layer code + key, # table key (layer or attribute code) + width, # line width + color, # line color + style, # line style + segs # list of segments (list of paths) + ) + +record feature ( # feature or waypoint + lat, # latitude + lon, # longitude + label # label + ) + + + +global arglist # list of arg records +global opts # command options +global chosen # cset of chosen layers + +global xmin, xmax, ymin, ymax # data range +global aspect # input coordinate aspect ratio + +global attrib # attribute style table +global slist # list of style records w/ seg lists +global pcolors # list of path background colors + +global features # list of feature records + +global logfile # feature log file, if any + + + +# main program + +procedure main(args) + local a, c, e, g, i, r, s, t, v + + # use large region sizes for better efficiency + collect(2, STRSIZE) # string region + collect(3, BLKSIZE) # block (heap) region + + # open window first, to validate and remove any window options + Window("label=--", "gamma=1.5", "bg=white", "fg=brown", + "resize=on", "canvas=hidden", WSIZE, args) + + randomize() + initattrib() + + # process command options + opts := options(args, "o:l:x:cdnpqt") + if \opts["o"] then { + if opts["o"] == "-" then + logfile := &output + else + logfile := open(opts["o"], "w") | stop("cannot write ", opts["o"]) + } + else + logfile := &output + + chosen := cset(\opts["l"]) | (&lcase -- 'n') # start with explicit layers + chosen ++:= 'go' # add paths & other data, if loaded + chosen --:= cset(\opts["x"]) # now apply exclusions + + # any remaining arguments are directory names + if *args = 0 then + stop("usage: ", &progname, " [options] dir...") + + # build list of arg records, classifying each filename or directory + arglist := [] + every s := !args do { + if directory(s) then + t := "dir" + else if iszip(s) then + t := "zip" + else + t := "txt" + put(arglist, arg(s, t, 1)) + } + + # scan text files first, because we haven't really done any validation + # (any unrecognized file is classified as a text file) + features := [] + every (a := !arglist) & (a.type == "txt") do + rdtext(a) + + # take inventory of DLG directories and files, and load XREF/NPnn info + every (a := !arglist) & (a.type ~== "txt") do { + inventory(a) + every r := !a.ltable do { + loadref(r) + if r.zone >= 0 then + loadcorners(r) + else + loadbounds(r) + } + if \opts["t"] & not traversed(!a.ltable) then + a.wanted := &null + lstats(a) + } + + if \opts["n"] then + return + (*(!arglist).ltable > 0) | stop("no data") + + # show initial screen + winit() + mapinit(draw, , xmin, xmax, ymax, ymin, aspect) + if WAttrib("label") == "--" then # set window label, if not specified + WAttrib("label=" || args[1]) + WAttrib("canvas=normal") # make window visible + Font("sans,bold,72") + Fg("pale yellowish gray") + DrawString(60, 120, "LOADING...") + + if \opts["c"] then # if just coverage wanted + chosen := 'n' # turn on names, turn off loaded paths + else { + + # finally: load in the data + alllabels() # show coverage while loading + every c := !DLG_LAYERS do # load by layers + every a := !arglist do + if \a.wanted then + loadlayer(\a.ltable[c]) + + # report memory usage + every put(c := [], &collections) + collect() + every put(a := [], &storage) + if /opts["q"] then { + write(" ", (a[2] + a[3] + MEGABYTE / 2) / MEGABYTE, + " MB loaded (", c[3], "+", c[4], " GC)") + } + } + + # put segment lists in order for drawing + # shuffle segments of each list to minimize "dead time" drawing offscreen + every put(slist := [], !attrib) + slist := sortf(slist, field(attrec, "seq")) + every g := (!slist).segs do + every !g :=: ?g # imperfect but good enough shuffle + + # report attribute counts, if -d given + if \opts["d"] then { + write() + every e := !slist do + if *e.segs > 0 then + write(right(e.seq, 3), ". ", e.lcode, " ", + left(e.key, 8), right(*e.segs, 7)) + write() + } + + # consume any events that may have occurred during loading + while *Pending() > 0 do + Event() + + # draw initial screen + EraseArea() + mapgen() + + # process interactive commands + repeat case e := Event() of { + &shift & &lpress: { logfeat(e) } + &shift & &rpress: { logfeat(e) } + &rpress: { locate() } + !"\n\r ": { mapgen() } + !"pP" | Key_PrSc: { print(); Bg("white") } + !"aA": { chosen := &lcase; mapgen() } + !"xX": { chosen := ''; EraseArea(); mapgen() } + !"qQ": { exit() } + any(&letters, e) & e: { + e := map(e) + if any(chosen, e) then { + chosen --:= e + EraseArea() + mapgen() + } + else { + chosen ++:= e + mapgen() + } + } + default: { mapevent(e) } + } +end + + + +# rdtext(arg) -- read a text file of paths and features + +procedure rdtext(arg) + local f, i, n, r, s, t, w, line + local lat, lon, alt, segs, points, nsegs, npts, nfeat + local xmn, xmx, ymn, ymx + static npaths + initial npaths := 0 + + f := open(arg.name) | stop("cannot open: ", arg.name) + s := "g" || (npaths % *pcolors + 1) + npaths +:= 1 + + segs := attrib[s].segs + nsegs := *segs + npts := 0 + nfeat := 0 + xmn := ymn := +180 * DEGSCALE + xmx := ymx := -180 * DEGSCALE + + points := [] + while line := read(f) do { # read line + every put(w := [], words(line)) # break into fields + # check first for path entry + if (lat:=real(w[-3])) & (lon:=real(w[-2])) & (alt:=real(w[-1])) & + (-90.<=lat<=90.) & (-180.<=lon<=180.) & (-1400<alt<30000) then { + npts +:= 1 + lon *:= DEGSCALE + lat *:= DEGSCALE + put(points, integer(lon), integer(lat)) + xmn >:= lon + ymn >:= lat + xmx <:= lon + ymx <:= lat + } + else if (lat := real(w[-2])) & (lon := real(w[-1])) & + (-90. <= lat <= 90.) & (-180. <= lon <= 180.) then { + npts +:= 1 + lon *:= DEGSCALE + lat *:= DEGSCALE + put(points, integer(lon), integer(lat)) + xmn >:= lon + ymn >:= lat + xmx <:= lon + ymx <:= lat + } + else { + # interrupt path sequence + if *points > 0 then { + put(segs, points) + points := [] + } + # check for feature (waypoint) label + if (lat := real(get(w))) & (lon := real(get(w))) & + (-90. <= lat <= 90.) & (-180. <= lon <= 180.) then { + nfeat +:= 1 + lon *:= DEGSCALE + lat *:= DEGSCALE + xmn >:= lon + ymn >:= lat + xmx <:= lon + ymx <:= lat + s := "" + while s ||:= " " || get(w) + put(features, feature(lat, lon, s[2:0])) + } + } + } + if *points > 0 then + put(segs, points) + + nsegs := *segs - nsegs + if nsegs = 0 & nfeat = 0 then + stop("no data: ", arg.name) + + r := layer("g", arg) + r.zone := -1 + r.datum := "WGS84" + r.ellipsoid := "WGS84" + r.icorners := r.ocorners := [xmn, ymn, xmn, ymx, xmx, ymx, xmx, ymn] + t := table() + t["g"] := r + arg.ltable := t + + n := 0 + every n +:= *segs[-nsegs to -1] + if /opts["q"] then + write(right(nsegs || ":" || npts || "+" || nfeat, 14), " ", lsumm(r)) + + close(f) + return +end + + + +# ddpopen(r, p) -- generate open DDF files from layer r matching pattern p + +procedure ddpopen(r, p) + local a, f, d, s, fname + + a := r.arg + every fname := !r.files do { + if not (map(fname) ? wild_match(p)) then + next + s := a.name || "/" || fname + f := &null + if a.type == "zip" then + f := zipfile(a.name, fname) + else + f := open(s, "ru") + d := ddfopen(\f) | stop("cannot open as DDF: ", s) + suspend d + } + fail +end + + + +# inventory(a) -- inventory arg entry a + +procedure inventory(a) + local b, c, f, fname, m, flist, trcount + + # load filenames into list, because we need to scan it twice + flist := [] + if a.type == "zip" then + f := zipdir(a.name) + else + f := open(a.name) + while put(flist, read(f)) + close(f) + + # count TR01LE??.DDF files + trcount := 0 + every fname := !flist do + if map(fname) ? (tab(-12) & ="tr01le") then + trcount +:= 1 + + # classify files and save the ones we want + a.ltable := table() + every fname := !flist do { + map(fname) ? { + while tab(upto('/') + 1) + pos(-12) | next + move(8) | next + =".ddf" | next + } + b := fname[-12:-4] | next + + every c := !lcodes(b, trcount) do { + if any(chosen, c) then { + # this is a wanted file in a wanted layer; remember it + /a.ltable[c] := layer(c, a, []) + put(a.ltable[c].files, fname) + } + } + } + + return +end + + + +# lcodes(basename, trcount) -- deduce layer code(s) from file basename + +procedure lcodes(basename, trcount) + local n, s, tr + + map(basename) ? { + if move(4) & ="a" & move(2) & any('f') then { + # xxxxAllF.DDF is layer ll attribute file + s := move(-2) + } + else if ="tr01" & =("le" | "np") & (n := integer(move(2))) then { + # TR01LEnn.DDF (or NPnn) is a transportation layer in a 100K map + if trcount > 12 then + s := ["mt", "rd", "rd", "rd", "rd", "rr"] [(n + 3) / 4] + else + s := ["mt", "rd", "rr"] [(n + 3) / 4] + } + else if move(2) & ="tr" & =("le" | "ne") & (n := integer(move(2))) then { + # xxTRLEnn.DDF (or NExx) is a transportation layer in state xx 250K map + s := ["mt", "rd", "rr"] [n % 3 + 1] + } + else { + move(2) + if any(&letters) then + s := move(2) # xxllyyyy is layer ll for state xx + else + s := move(-2) # ll01xxxx is layer ll otherwise + } + } + + return case s of { + "bd": "b" # boundaries (BD: boundaries) + "hp": "c" # contours (HP: hypsography) + "nv": "d" # sand etc. (NV: nonvegetative features) + "pl": "l" # land sections (PL: public lands) + "sm": "m" # markers (SM: survey markers) + "rd": "r" # roads (RD: roads) + "ms": "s" # structures (MS: manmade structures) + "rr": "t" # train tracks (RR: railroads) + "mt": "u" # utilities (MT: miscellaneous transportation) + "tr": "rtu" # transportatn (TR: transportation, shared by r/t/u) + "sc": "v" # vegetation (SC: surface cover) + "hy": "w" # water (HY: hydrology) + default: "o" # other + } +end + + + +# getdata(r, p, l) -- get data vector l of layer r using file pattern p + +procedure getdata(r, p, l) + local ddfile, d, e, zone + + ddfile := ddpopen(r, p) | + stop("no file ", p, " for layer ", r.lcode, ": ", r.arg.name) + while d := ddfread(ddfile) do + every e := !d do + if e[1] == l then + break break + ddfclose(ddfile) + return e +end + + + +# loadref(r) -- load XREF and IREF files for layer r of arg a + +procedure loadref(r) + local e + + e := getdata(r, "*iref.ddf", "IREF") + until get(e) == "BI32" + r.xscale := real(get(e)) + r.yscale := real(get(e)) + + e := getdata(r, "*xref.ddf", "XREF") + case e[5] of { + "NAS": r.datum := "NAD27" # North American 1927 + "NAX": r.datum := "NAD83" # North American 1983 + "WGA": r.datum := "WGS60" # World Geodetic System 1960 + "WGB": r.datum := "WGS66" # World Geodetic System 1966 + "WGC": r.datum := "WGS72" # World Geodetic System 1972 + "WGE": r.datum := "WGS84" # World Geodetic System 1984 + default: r.datum := "?????" # unrecognized + } + if e[4] == "UTM" then + r.zone := integer(e[6]) + else + r.zone := -1 + + return +end + + + +# loadbounds(r) -- load SPDM file to determine range of locations +# +# (SPDM files are used with 250K DLG layers) + +procedure loadbounds(r) + local e, xmn, xmx, ymn, ymx + + e := getdata(r, "*spdm.ddf", "DMSA") + get(e) + xmn := get(e) * r.xscale * DEGSCALE + ymn := get(e) * r.yscale * DEGSCALE + xmx := get(e) * r.xscale * DEGSCALE + ymx := get(e) * r.yscale * DEGSCALE + r.ellipsoid := "Clarke66" + r.icorners := r.ocorners := [xmn, ymn, xmn, ymx, xmx, ymx, xmx, ymn] + return +end + + + +# loadcorners(r) -- load NPnn file to determine corner points +# +# (NPnn files are used with 24K and 100K DLG layers) + +procedure loadcorners(r) + local ddfile, d, e, i, x, y, L, C66, G80 + + every ddfile := ddpopen(r, "*np??.ddf") do { + L := [] + while d := ddfread(ddfile) do + every e := !d do + if get(e) == "SADR" then + while put(L, get(e)) + ddfclose(ddfile) + r.icorners := cmerge(r.icorners, L) + } + + if /r.icorners then + stop("no NPnn file for layer ", r.lcode, ": ", r.arg.name) + + # infer ellipsoid of UTM projection + L := [] + every i := 1 to *r.icorners by 2 do { + x := (r.icorners[i] * r.xscale - 500000.0) + y := (r.icorners[i+1] * r.yscale) + put(L, r.zone, x, y) + } + C66 := project(invp(utm("Clarke66")), L) + G80 := project(invp(utm("GRS80")), L) + + if quadfit(C66) < quadfit(G80) then { + r.ellipsoid := "Clarke66" + r.ocorners := project(molodensky("NAD27", "NAD83"), C66) + } + else { + r.ellipsoid := "GRS80" + r.ocorners := G80 + } + + every !r.ocorners *:= DEGSCALE + return +end + + + +# cmerge(A, B) -- merge two corners lists +# +# Assumes that the corner order is [SW, NW, NE, SE] +# and takes the more extreme value for each coordinate. + +procedure cmerge(A, B) + local C + + if /A | /B then return \A | \B + C := [] + + if A[1] + A[2] < B[1] + B[2] then + put(C, A[1], A[2]) + else + put(C, B[1], B[2]) + + if A[3] - A[4] < B[3] - B[4] then + put(C, A[3], A[4]) + else + put(C, B[3], B[4]) + + if A[5] + A[6] > B[5] + B[6] then + put(C, A[5], A[6]) + else + put(C, B[5], B[6]) + + if A[7] - A[8] > B[7] - B[8] then + put(C, A[7], A[8]) + else + put(C, B[7], B[8]) + + return C +end + + + +# quadfit(L) -- proximity of coordinate in L to multiple of 1/8 + +procedure quadfit(L) + local i, mn, mx, a, b + + mn := 1.0 + every i := 1 to *L by 2 do { + a := L[i] * 8 + b := L[i+1] * 8 + mx := max(abs(a - round(a)), abs(b - round(b))) + mn := min(mn, mx) + } + return mn +end + + + +# lstats(a) -- report statistics for the layers of arg a + +procedure lstats(a) + local c, d, g, k, l, n, r, v, z + + if \opts["q"] then + return + + # group by identical projection attributes + g := table('') + every r := !a.ltable do { + k := lsumm(r) + g[k] ++:= r.lcode + } + + # report consistent layers together on one line + l := sort(g, 3) + while k := get(l) do { + v := get(l) + writes(if /a.wanted then "X" else " ") + writes(if *g = 1 then " " else "*") + every c := !cset(DLG_LAYERS) do # list alphabetically + writes(if upto(v, c) then c else "-") + write(" ", k) + } + return +end + + + +# lsumm(r) -- return one-line layer info summary + +procedure lsumm(r) + return r.datum[1] || r.datum[-2:0] || " " || + (if r.zone < 0 then "LL" else right(r.zone, 2)) || " " || + r.ellipsoid[1] || r.ellipsoid[-2:0] || " " || + right(degc(r.ocorners[-1]), 3) || " " || + left(degc(r.ocorners[-2]), 4) || " " || + r.arg.name +end + + + +# degc(d) -- code degree measurement as nnnx where x is a-h for 0/8 to 7/8 + +procedure degc(d) + local n, x + + d := abs(d / DEGSCALE) + 0.0625 # 1/16 for rounding + n := integer(d) + x := "abcdefgh" [1 + integer(8 * (d - n))] + return n || x +end + + + +# field(constr, key) -- given record constructor, find index of named field + +procedure field(constr, key) + local i, r + image(constr) ? ="record constructor" | fail + r := constr() + every i := 1 to *r do + r[i] := i + return r[key] +end + + + +# traversed(r) -- check whether layer r is traversed by a path + +procedure traversed(r) + local k, i, segs, pts, xmin, xmax, ymin, ymax + + xmin := xmax := r.ocorners[1] + every xmin >:= r.ocorners[3 | 5 | 7] + every xmax <:= r.ocorners[3 | 5 | 7] + ymin := ymax := r.ocorners[2] + every ymin >:= r.ocorners[4 | 6 | 8] + every ymax <:= r.ocorners[4 | 6 | 8] + + every k := key(attrib) do + if k ? (="g" & tab(many(&digits)) & pos(0)) then + every pts := !attrib[k].segs do + every i := 1 to *pts by 2 do + if xmin < pts[i] < xmax & ymin < pts[i+1] < ymax then + return + + fail +end + + + +# loadlayer(r) -- load one layer of files + +procedure loadlayer(r) + local p, attid, ddfile + + setdraw(attrib[r.lcode]) + drawlabel(r) + + attid := table() + every ddfile := ddpopen(r, "*a??f.ddf") do { + loadatts(ddfile, r, attid) + ddfclose(ddfile) + } + + every ddfile := ddpopen(r, "*ne??.ddf" | "*le??.ddf") do { + loadpts(ddfile, r, attid) + ddfclose(ddfile) + } + + return +end + + + +# loadatts(ddfile, r, attid) -- load attribute ID table + +procedure loadatts(ddfile, r, attid) + local d, e, i, k, n, s, v + + n := -1 + if r.lcode == "t" then + i := [1, 7] # for RR, append tunnel and rapid transit flags + else + i := [] + + while d := ddfread(ddfile) do { + k := &null + every e := !d do { + s := get(e) + if s == "ATPR" then + k := get(e) || get(e) + else if s == "ATTP" then { + v := get(e) + every \v ||:= (" " ~== e[!i]) + attid[\k] := v + if (n +:= 1) % PINTERVAL = 0 then + progress(r) + } + } + } + return +end + + + +# loadpts(ddfile, r, attid) -- load coordinate file into memory + +procedure loadpts(ddfile, r, attid) + local a, d, e, i, k, m, n, p, s, v, vv, x, y + local lcode, zone, coords, arec + + lcode := r.lcode + zone := r.zone + + if zone >= 0 then { # if not already in lat/lon form + if /opts["p"] then { # if no -p option + p := pptrans(r.icorners, r.ocorners) # use approx, faster projection + zone := &null # indicate such for code below + } + else { + p := invp(utm(r.ellipsoid)) # use full inverse-UTM projection + if r.ellipsoid == "Clarke66" then # and if needed, + p := compose(molodensky("NAD27", "NAD83"), p) # datum conversion + } + } + + n := 0 + while d := ddfread(ddfile) do { + a := lcode || "-" + v := [] + coords := [] + every e := !d do { + if *e < 3 then + next + s := get(e) + if s == "ATID" then { + k := get(e) || get(e) + while k[4] ~== "F" do + k := get(e) || get(e) | break + a := \attid[k] | lcode + } + else if s == "SADR" then { + if /p then { + # latitude/longitude input + while x := get(e) & y := get(e) do + put(v, x * r.xscale * DEGSCALE, y * r.yscale * DEGSCALE) + } + else if /zone then { + # using approximate projection, which includes scaling + while x := get(e) & y := get(e) do + put(coords, x, y) + } + else { + # full inverse UTM projection + while x := get(e) & y := get(e) do + put(coords, zone, x * r.xscale - 500000.0, y * r.yscale) + } + } + } + + if \p then { # if projection needed + coords := project(p, coords) # project UTM to lat/lon + m := if /zone then 1 else DEGSCALE # select multiplier + while put(v, integer(m * get(coords))) # convert to scaled integer + } + + if *v = 0 then + next + + if not (arec := \attrib[a]) then { + # add unrecognized attribute code to table + arec := copy(attrib[lcode]) + arec.key := a || "*" # "*" indicates unregistered attribute + arec.segs := [] + attrib[a] := arec + } + + while *v > MAXDRAW do { # break too-large path into pieces + vv := [] + every 3 to MAXDRAW by 2 do + put(vv, get(v), get(v)) # move out of v + put(vv, v[1], v[2]) # leave one point for overlap + put(arec.segs, vv) # store extracted piece + } + + # loops are rare in the data, but can crash XFree86 server if dashed + if v[1] = v[-2] & v[2] = v[-1] then { # if loop + put(v, v[3], v[4]) # overshoot to 2nd point again + } + put(arec.segs, v) # store what's left of original + + if (n +:= 1) % PINTERVAL = 0 then + progress(r) + + } + return +end + + + +# logfeat() -- record current location to log file + +procedure logfeat(e) + local ll, lat, lon, locn, label + + until Event() === (&lrelease | &rrelease) # wait for button up + ll := project(invp(mapproj()), [&x + 0.5, &y + 0.5]) # cvt coords to lat/lon + lon := get(ll) / DEGSCALE + lat := get(ll) / DEGSCALE + locn := frn(lat, 0, 6) || " " || frn(lon, 0, 6) + label := "" + + if e === &lpress then { # if named (not anonymous), ask for label + setdraw(attrib["DIALOG"]) + VSetFont() + case TextDialog( + ["Enter label for", locn || ":"], , , 30, ["Okay", "Cancel"]) of { + "Okay": label := " " || get(dialog_value) + "Cancel": fail + } + put(features, feature(DEGSCALE * lat, DEGSCALE * lon, label[2:0])) + if any(chosen, "f") then + allfeats(mapproj(), Pending) # redraw feats to display label + } + + write(logfile, locn, label) + flush(logfile) + return +end + + + +# locate() -- display location while right button is held down + +$define BOXW 265 # popup box width +$define BOXH 90 # popup box height +$define SMAX (BOXW - 40) # maximum scalebar length + +procedure locate() + + setdraw(attrib["DIALOG"]) # set colors and font for drawing + Font("mono,bold,14") + + if &x < BOXW + 40 & &y < BOXH + 40 then + Popup(20, WAttrib("height") - BOXH - 20, BOXW, BOXH, locproc, mapproj()) + else + Popup(20, 20, BOXW, BOXH, locproc, mapproj()) + + return +end + + + +# locate(wproj) -- calculate scale and location using caller's projection + +procedure locproc(wproj) + local d, e, m, s, u, cx, dx, dy, ll, lat, lon, winv + + winv := invp(wproj) # get projection from screen to lat/lon + dx := WAttrib("dx") # get popup box coordinate system + dy := WAttrib("dy") + + # compute a reasonably round length that works for a scale bar + u := 90 * DEGSCALE / 1e7 # one meter, in latitude units + m := sbsize(wproj, xmin, ymin, u, SMAX) + + # draw the scale bar + ll := project(wproj, [xmin, ymin, xmin + m * u, ymin]) + d := ll[3] - ll[1] + cx := BOXW / 2 + FillRectangle(cx - d / 2, 55, d, 8) + + if m >= 1000 then + s := (m / 1000) || " km" + else + s := m || " m" + CenterString(cx, 70, s) + + # give coordinates of mouse location until button released + until e === &rrelease do { + + ll := project(winv, [&x + 0.5, &y + 0.5]) # cvt screen coords to lat/lon + lon := get(ll) / DEGSCALE # and scale from integer to real + lat := get(ll) / DEGSCALE + + GotoRC(1, 1) + WWrites("\n ", dms(lat, "S", "N"), frn(lat, 13, 6)) + WWrites("\n ", dms(lon, "W", "E"), frn(lon, 13, 6)) + + e := Event() # get next event + &x +:= dx # remove effect of popup box coordinate system + &y +:= dy + } + + return +end + +procedure dms(n, s1, s2) + local deg, min, sec + + if n < 0 then + n := -n + else + s1 := s2 + + n +:= 1 / 7200. # rounding + deg := integer(n); n := (n - deg) * 60 + min := integer(n); n := (n - min) * 60 + sec := integer(n) + + return s1 || right(deg, 4) || "\260" || right(min, 2, "0") || "'" || + right(sec, 2, "0") || "\"" +end + + + +# draw(win, pjn) -- draw all selected map layers, without erasing first + +procedure draw(win, pjn) + local a, d, v, arec + + every (arec := !slist) & any(chosen, arec.lcode) do { + setdraw(arec) | next + every d := !arec.segs do { + v := project(pjn, d) # project to window x/y coords + every !v >:= 30000.0 # clamp to legal X values allowing dx/dy + every !v <:= -30000.0 # clamp as floating to avoid lgint bug + if *v = 2 then + FillRectangle(v[1] - 1, v[2] - 1, 3, 3) + else + DrawLine ! v + if *Pending() > 0 then + return + } + } + + # draw feature (waypoint) labels + if any(chosen, "f") then + allfeats(pjn, Pending) + + # draw pseudo-layer "n" + if any(chosen, "n") then + alllabels(Pending) + + collect() # do this now, while awaiting input + return +end + + + +# winit() -- initialize window configuration + +procedure winit() + local a + + xmin := ymin := +180 * DEGSCALE + xmax := ymax := -180 * DEGSCALE + every a := !arglist do + if \a.wanted then { + every xmin >:= (!a.ltable).ocorners[1 | 3] + every xmax <:= (!a.ltable).ocorners[5 | 7] + every ymin >:= (!a.ltable).ocorners[2 | 8] + every ymax <:= (!a.ltable).ocorners[4 | 6] + } + aspect := cos(dtor((ymax + ymin) / (2 * DEGSCALE))) + return +end + + + +# allfeats(pjn, p) -- draw feature labels +# +# p is Pending procedure, if to check and quit early + +procedure allfeats(pjn, p) + local f, x, y, xy, xy2 + + xy := [] + every f := !features do + put(xy, f.lon, f.lat) + xy := project(pjn, xy) + xy2 := copy(xy) + + Font("sans, bold, 10") + setdraw(attrib["f"]) + Fg("white") # draw offset backgrounds in white + every f := !features do { + DrawString(get(xy2) + 4, get(xy2) + 5, f.label) + if *(\p)() > 0 then + break + } + + setdraw(attrib["f"]) # draw labels in black + every f := !features do { + x := get(xy) + y := get(xy) + FillRectangle(x - 1, y - 1, 3, 3) + DrawString(x + 5, y + 4, f.label) + if *(\p)() > 0 then + break + } + + return +end + + + +# alllabels(p) -- draw labels for all layers in standard color +# +# p is Pending procedure, if to check and quit early + +procedure alllabels(p) + local a, r + + setdraw(attrib["n"]) + every a := !arglist do { + if \a.wanted then { + drawlabel(!a.ltable) # pick any layer + if \opts["c"] then { + drawcoverage(a) + setdraw(attrib["n"]) + } + } + if *(\p)() > 0 then + break + } + return +end + + + +# drawlabel(r) -- draw label for layer r in current color +# +# sets r.px, r.py to progress bar position and r.wd to layer width + +procedure drawlabel(r) + local x, y, w, h, n, d, u, s, tw, tmax, v, wproj + static lc, uc + initial { + lc := string(&lcase) + uc := string(&ucase) + } + + # draw the bounding box + wproj := mapproj() + v := copy(r.ocorners) + put(v, r.ocorners[1], r.ocorners[2]) + v := project(wproj, v) # project to window x/y coords + every !v >:= 30000.0 # clamp to legal X values allowing dx/dy + every !v <:= -30000.0 # clamp as floating to avoid lgint bug + DrawLine ! v + + # find the center and range + x := (v[1] + v[3] + v[5] + v[7]) / 4 + y := (v[2] + v[4] + v[6] + v[8]) / 4 + w := (v[5] + v[7] - v[1] - v[3]) / 2 + h := (v[4] + v[6] - v[2] - v[8]) / 2 + + # trim the name + s := r.arg.name + while s[-1] == "/" do + s := s[1:-1] + s ? { + while tab(upto('/') + 1) + s := map(tab(0), lc, uc) + } + if s[-4:0] == (".ZIP" | ".GPS" | ".RTE" | ".TRK") then + s := s[1:-4] + + # draw the label + Font("sans,bold," || MAXFONT) + tw := TextWidth(s) + tmax := .90 * w + if tw > tmax then { + n := integer(MAXFONT * tmax / tw) + if n <:= MINFONT then { + # it doesn't fit, and will overlap neighbors with minimum font size; + # add pseudorandom vertical offset to mitigate overlap + d := abs(r.ocorners[7] / DEGSCALE) # SE corner longitude + u := integer(8 * d + 0.5) # 1/8-degree units + u +:= integer(2 * d + 0.5) # half-degree units + y -:= 0.20 * h * (1.5 - u % 4) + } + if n < MINBOLD then + Font("sans," || n) + else + Font("sans,bold," || n) + } + CenterString(x, y, s) + + r.px := integer(x) + r.py := integer(y + 0.75 * WAttrib("fheight")) + r.wd := w + return +end + + + +# progress(r) -- draw progress square for layer r + +procedure progress(r) + local a, x + + a := r.arg + a.pcount := (\a.pcount + 1) | 0 + x := r.px + PSQSIZE * (a.pcount % PSQUARES - PSQUARES / 2) + FillRectangle(x, r.py, PSQSIZE - PSQGAP, PSQSIZE - PSQGAP) + if (a.pcount / PSQUARES) % 2 = 1 then + EraseArea(x + 1, r.py + 1, PSQSIZE - PSQGAP - 2, PSQSIZE - PSQGAP - 2) + return +end + + + +# drawcoverage(a) -- draw coverage indicators for arg entry a + +procedure drawcoverage(a) + local c, r, x, y, w + + r := \!a.ltable | return + w := r.wd / *DLG_LAYERS + w >:= PSQSIZE + w <:= 2 + x := r.px - (w * *DLG_LAYERS) / 2 + y := r.py + + every c := !cset(DLG_LAYERS) do { + if r := \a.ltable[c] then { + setdraw(attrib[r.lcode]) + FillRectangle(x, y, w, w) + } + x +:= w + } + return +end + + + +# print() -- print visible portion to file + +procedure print() + local psname, psfile + + Bg("pale weak brown") + VSetFont() + setdraw(attrib["DIALOG"]) # set reasonable colors for dialog + repeat case OpenDialog("Print to file:") of { + "Okay": { + if *dialog_value = 0 then + next + if close(open(psname := dialog_value)) then + case TextDialog("Overwrite existing file?", , , , + ["Yes", "No", "Cancel"]) of { + "Yes": &null + "No": next + "Cancel": fail + } + if psfile := open(psname, "w") then + break + case TextDialog("Cannot write " || psname) of { + "Okay": next + "Cancel": fail + } + } + "Cancel": + fail + } + + Popup(, , 300, 50, + popwrite, [psfile, mapproj(), WAttrib("width"), WAttrib("height")]) + close(psfile) + return +end + +procedure popwrite(psargs) + CenterString(150, 25, "Writing PostScript...") + return writeps ! psargs +end + +procedure writeps(psfile, projn, wwidth, wheight) + local arec, color, style, width, ptoff, xmax, ymax, xmul, ymul + local a, b, f, m, w, h, pj, d, s, u, v, x, y, dx, dy, fx, fy, ll + + b := project(invp(projn), [0, 0, wwidth, wheight]) + xmax := PSSCALE * wwidth + ymax := PSSCALE * wheight + xmul := xmax / (b[3] - b[1]) + ymul := ymax / (b[2] - b[4]) + pj := rectp(b[1], b[4], 0, 0, xmul, ymul) # set projection + + ptoff := PSPT / 2 + s := " 0 " || PSPT || " rlineto" + s ||:= " " || PSPT || " 0 rlineto" + s ||:= " 0 -" || PSPT || " rlineto" + + epsheader(psfile, 0, 0, PSSCALE * wwidth, PSSCALE * wheight, "r") + every write(psfile, ![ + "1 setlinecap", + "/cdivr { 65535 div 3 1 roll } bind def", + "/color { cdivr cdivr cdivr setrgbcolor } bind def", + "/solid { [] 0 setdash } bind def", + "/dashed { [ .04 inch dup ] 0 setdash } bind def", + "/m { moveto } bind def", + "/r { rlineto } bind def", + "/s { rlineto stroke } bind def", + "/p { moveto" || s || " fill } bind def", + "/f { 2 copy p moveto 48 -36 rmoveto show } bind def", + ]) + + every (arec := !slist) & any(chosen, arec.lcode) do { + if *arec.segs = 0 | arec.width < 0 then + next + if color ~===:= arec.color then + write(psfile, map(ColorValue(arec.color), ",", " "), " color") + if width ~===:= arec.width then + write(psfile, arec.width / real(PSLWI), " inch setlinewidth") + if style ~===:= arec.style then + write(psfile, style) + every d := !arec.segs do { + v := project(pj, d) + if *v = 2 then { + x := integer(get(v)) + y := integer(get(v)) + if (0 <= x < xmax) & (0 <= y < ymax) then + write(psfile, x - ptoff, " ", y - ptoff, " p") + next + } + v := Coalesce(ClipLine(v, 0, 0, xmax, ymax)) | next + every a := !v do { + x := integer(get(a)) + y := integer(get(a)) + fy := integer(pull(a)) + fx := integer(pull(a)) + write(psfile, x, " ", y, " m") + while dx := integer(get(a) - x) do { + dy := integer(get(a) - y) + write(psfile, dx, " ", dy, " r") + x +:= dx + y +:= dy + } + write(psfile, fx - x, " ", fy - y, " s") + } + } + } + + # write features + if *features > 0 & any(chosen, "f") then { + write(psfile) + write(psfile, "/Times-Roman findfont 120 scalefont setfont") + write(psfile, "0 0 0 color") + every f := !features do { + a := project(pj, [f.lon, f.lat]) + x := integer(get(a)) + y := integer(get(a)) + if (0 <= x <= xmax) & (0 <= y <= ymax) then + write(psfile, "(", psprotect(f.label), ") ", + x - ptoff, " ", y - ptoff, " f") + } + } + + # write scale bar + u := 90 * DEGSCALE / 1e7 # one meter, in latitude units + m := sbsize(pj, xmin, ymin, u, 2000) + ll := project(pj, [xmin, ymin, xmin + m * u, ymin]) + d := ll[3] - ll[1] + if m >= 1000 then + s := (m / 1000) || " km" + else + s := m || " m" + + every write(psfile, ![ + "", + "0 0 0 color", + "0 0 m 0 120 r " || d || " 0 r 0 -120 r fill", + "/Helvetica findfont 100 scalefont setfont", + "65535 65535 65535 color", + integer(d / 2 - 120) || " 25 m (" || s || ") show", + ]) + + write(psfile, "showpage") + return +end + + + + +# initattrib() -- initialize drawing attributes +# +# IMPORTANT: Map entities are drawn in the order of the def() calls below. + +procedure initattrib() + local i, s + +$define ROUTE "magenta-red" # path foreground color + pcolors := [ # path background colors + "yellow", # yellow + "light green", # green + "light bluish cyan", # blue + "reddish yellow", # orange + "pale purple", # purple + "pale red-yellow", # peach + "pale moderate green", # greenish gray + "pale moderate cyan", # bluish gray + ] + pull(pcolors) # remove trailing null + + attrib := table() + deflayer(" ", "black") + def("SWEEP", 3, "reddish orange") # interactive sweeping with mouse + def("DIALOG", 1, "black") # dialog boxes + + every i := 1 to *pcolors do { + s := "g" || i + deflayer(s, ROUTE) # paths (first drawing) + def(s || "b", 10, pcolors[i]) # faint, wide highlighter background + def(s || "f", 2, ROUTE) # bold foreground + } + + deflayer("b", "light reddish yellow") # boundaries (wide, so draw first) + def("b", 3) + + deflayer("o", Fg()) # unknown other data; use specified Fg + def("o") + + deflayer("c", "light red-yellow") # contour lines (hypsography) + def("c-", , "pale moderate red-yellow") # deemphasize unattributed segments + def("c") # contour line + def("0200205", , "light moderate bluish-cyan") # bathymetric contour + def("0200206", , "light moderate bluish-cyan") # depth curve + def("0200210", , "light moderate bluish-cyan") # suppl bathymetric contour + def("0200207", , "deep red-yellow") # watershed (e.g. continental) divide + + deflayer("l", "pale whitish red") # land sections + def("l") + + deflayer("v", "light green") # vegetation (surface cover) + def("v") # surface cover + + deflayer("d", "light weak green") # gravel etc. (nonvegetative features) + def("d") + + deflayer("w", "bluish cyan") # water (hydrology) + def("w-", , "pale bluish cyan") # deemphasize unattributed segments + def("0500415", , , "dashed") # aqueduct or water pipeline + def("0500200", 2) # shoreline + def("0500201", 2) # manmade shoreline + def("w") # unspecified hydrology + def("0500412") # stream + + deflayer("s", "weak reddish yellow") # manmade structures + def("2000299", , "pale reddish yellow") # processing line + def("s") + def("s-") # uattributed, incl building outlines + def("2000400") # buildings as point nodes + def("2000202", , "light moderate reddish yellow") # wall + def("2000206", , "light moderate reddish yellow") # fence + + deflayer("r", "deep gray") # roads and trails + def("r-", , "pale gray") # deemphasize unattributed segments + def("1700201", 3, "black") # road, primary, undivided + def("1700202", 3, "black") # road, primary, divided + def("1700203", 2, "black") # road, primary, one of divided paths + def("1700204", 2, "black") # road, primary, one-way + def("1700205", 2, "black") # road, secondary + def("1700206", 2, "black") # road, secondary + def("1700207", 2, "black") # road, secondary + def("1700208", 2, "black") # road, secondary + def("1700214", 1, "black", "dashed") # ferry route + def("1700218") # road, class 3, divided + def("1700209") # road, class 3, undivided + def("1700402") # entrance ramp + def("r") # unspecified road or trail + def("1700210", , , "dashed") # road, class 4 + def("1700219", , , "dashed") # road, class 4, one-way + def("1700212", , , "dashed") # road, class 5, 4WD + def("1700211", , "dark red", "dashed") # trail + def("1700213", , "dark red", "dashed") # footbridge + + deflayer("t", "dark orange") # railroads + def("t-", , "pale weak orange") # deemphasize unattrib segments + def("t") # unspecified railroad + def("1800201", 2) # railroad main + def("1800201E", 2) # railroad main elevated + def("1800201R", 2) # railroad main on drawbridge + def("1800201T", 2, , "dashed") # railroad main in tunnel + def("1800207", 1, , "dashed") # railroad ferry route + def("1800208", 1) # railroad siding + def("1800209", 2) # railroad yard + def("1800400", 1) # railroad station +$define TRANSIT "dark blue" + def("1800201Y", 2, TRANSIT) # rapid transit rail main + def("1800201EY", 2, TRANSIT) # rapid transit main elevated + def("1800201RY", 2, TRANSIT) # rapid transit main on drawbrg + def("1800201TY", 2, TRANSIT, "dashed") # rapid transit main in tunnel + def("1800202Y", 2, TRANSIT) # rapid transit main in road + def("1800202RY", 2, TRANSIT) # rapid transit, in road on drawbridge + def("1800208Y", 1, TRANSIT) # rapid transit siding + def("1800400Y", 1, TRANSIT) # rapid transit station + + deflayer("u", "light gray") # misc transpt: power, pipe, airport + def("u") + def("u-", , "white-gray") # unattrib segments incl airport runways +$define UTILITY "strong purple-magenta" + def("1900201", 1, UTILITY, "dashed") # petroleum pipeline + def("1900202", 1, UTILITY) # power line + def("1900203", 1, UTILITY) # phone line + def("1900400", 1, UTILITY) # power plant + def("1900401", 1, UTILITY) # substation + def("1900402", 1, UTILITY) # hydro plant + def("1900403", 1, "light gray") # landing strip or airport + def("1900404", 1, "orange") # helipad + def("1900405", 1, "light gray") # launch complex + + deflayer("m", "blue") # survey markers + def("m-", , "pale weak blue") # deemphasize unattributed lines + def("m") + + deflayer("f", "black") # feature labels + def("f") + + deflayer("n", "deep green") # file labels + def("n") + + deflayer("g", ROUTE) # paths (retraced) + + every i := 1 to *pcolors do { # link ea GPS bg/fg/bg set to one list + s := "g" || i + def(s, 2) + attrib[s || "b"].segs := attrib[s].segs + attrib[s || "f"].segs := attrib[s].segs + } + + return +end + + + +# deflayer -- define layer code and default color for subsequent defs + +global layercode, layercolor + +procedure deflayer(lcode, color) + layercode:= lcode + layercolor := color + return +end + + + +# def(key, width, color, style) -- define style info for code or attribute +# +# default width is 1 +# default color is as last set by deflayer() +# default style is "solid" +# +# a key of "x" matches undefined attributes of layer x +# a key of "x-" matches segments without attributes +# +# a width of -1 means "don't draw" + +procedure def(key, width, color, style) + static seq + initial seq := 0 + + /width := 1 + /color := layercolor + /style := "solid" + attrib[key] := attrec(seq +:= 1, layercode, key, width, color, style, []) + return +end + + + +# setdraw(arec) -- set color, linewidth, linestyle based on attribute record +# +# fails if width is negative, meaning that drawing is to be suppressed + +procedure setdraw(arec) + if arec.width < 0 then + fail + WAttrib("fg=" || arec.color, + "linewidth=" || arec.width, "linestyle=" || arec.style) + return +end diff --git a/ipl/gprogs/drawup.icn b/ipl/gprogs/drawup.icn new file mode 100644 index 0000000..4b9c0db --- /dev/null +++ b/ipl/gprogs/drawup.icn @@ -0,0 +1,88 @@ +############################################################################ +# +# File: drawup.icn +# +# Subject: Program to create draft from drawdown +# +# Author: Ralph E. Griswold +# +# Date: January 23, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces an ISD from a bi-level image string or row file. +# +# The following option is supported: +# +# -n s draft name, default "drawup" +# +# -r interpret input as row pattern; default image string +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, patutils, patxform, weavutil, xcode +# +############################################################################ + +link options +link patutils +link patxform +link weavutil +link xcode + +procedure main(args) + local threading, treadling, rows, pattern, i + local symbols, symbol, drawdown, draft, opts + + opts := options(args, "rn:") + + if \opts["r"] then { + drawdown := [] + while put(drawdown, read()) + } + else drawdown := pat2rows(read()) | stop("*** invalid input") + + treadling := analyze(drawdown) + drawdown := protate(drawdown, "cw") + threading := analyze(drawdown) + + symbols := table("") + + every pattern := !treadling.patterns do { + symbol := treadling.rows[pattern] + symbols[symbol] := repl("0", *threading.rows) + pattern ? { + every i := upto('1') do + symbols[symbol][threading.sequence[i]] := "1" + } + } + + symbols := sort(symbols, 3) + rows := [] + + while get(symbols) do + put(rows, get(symbols)) + + draft := isd() + + draft.name := \opts["n"] | "drawup" + draft.threading := threading.sequence + draft.treadling := treadling.sequence + draft.warp_colors := list(*threading.sequence, 1) + draft.weft_colors := list(*treadling.sequence, 2) + draft.color_list := ["black", "white"] + draft.shafts := *threading.rows + draft.treadles := *treadling.rows + draft.tieup := rows + + xencode(draft, &output) + +end diff --git a/ipl/gprogs/drip.icn b/ipl/gprogs/drip.icn new file mode 100644 index 0000000..6e1c775 --- /dev/null +++ b/ipl/gprogs/drip.icn @@ -0,0 +1,150 @@ +############################################################################ +# +# File: drip.icn +# +# Subject: Program to demonstrate color map animation +# +# Author: Gregg M. Townsend +# +# Date: May 31, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# usage: drip [-n ncolors] [-c correlation] [-d delay] [window options] +# +# drip uses color map animation to simulate the spread of colored +# liquid dripping into the center of a pool. +# +# ncolors is the number of different colors present at one time. +# +# correlation (0.0 to 1.0) controls the similarity of two consecutive +# colors. It probably doesn't meet a statistician's strict definition +# of the term. +# +# delay is the delay between drops, in milliseconds. This may not be +# needed; speed seems to vary greatly among different X servers, even on +# the same machine. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: evmux, options, optwindw, random +# +############################################################################ + + +link evmux +link options +link optwindw +link random + +global opttab + +procedure main(args) + local win, mono, w, h, m, d + local a, r, i, xscale, yscale, rad, xctr, yctr, xrad, yrad + local cindex, cspec, ncolors, bg + + # process options + opttab := options(args, winoptions() || "n+d+c.") + /opttab["B"] := "black" + /opttab["W"] := 512 + /opttab["H"] := 512 + /opttab["M"] := -1 + /opttab["d"] := 50 + /opttab["n"] := 32 + /opttab["c"] := 0.8 + win := optwindow(opttab, "cursor=off", "echo=off") + w := opttab["W"] + h := opttab["H"] + m := opttab["M"] + ncolors := opttab["n"] + d := opttab["d"] + + # calculate radius of circle and limit number of colors to that + r := h / 2 + r >:= w / 2 + xscale := (w / 2.0) / r + yscale := (h / 2.0) / r + ncolors >:= r + + # get background color as string of 3 integers (works faster that way) + bg := ColorValue(win, opttab["B"]) + + # allocate a set of mutable colors, initialized to the background + cindex := list() + every 1 to ncolors do + put(cindex, NewColor(win, bg)) + if *cindex = 0 then + stop("can't allocate mutable colors") + if ncolors >:= *cindex then + write(&errout, "proceeding with only ", ncolors, " colors") + + # make list of radii, with a minimum difference of 1 + # try to equalize the *areas* of the rings, not their widths + a := &pi * r * r + rad := list(ncolors) + every i := 1 to *rad do + rad[i] := integer(sqrt((a * i) / (ncolors * &pi)) + 0.5) + every i := 1 to *rad-1 do + rad[i] >:= rad[i+1] - 1 + + # draw nested circles (in different mutable colors all set to the background) + xctr := m + w / 2 + yctr := m + h / 2 + every i := *rad to 1 by -1 do { + Fg(win, cindex[i]) + xrad := xscale * rad[i] + yrad := yscale * rad[i] + FillArc(win, xctr - xrad, yctr - yrad, 2 * xrad, 2 * yrad) + } + WFlush(win) + + # install a sensor to exit on q or Q + quitsensor(win) + + # drip colors into the center and watch them spread, + # checking for events each time around + cspec := list(ncolors, bg) + repeat { + while *Pending(win) > 0 do + evhandle(win) + if d > 0 then { + WFlush(win) + delay(d) + } + pull(cspec) + push(cspec, newcolor()) + every i := 1 to *cspec do + Color(win, cindex[i], cspec[i]) + } + +end + + +# newcolor -- return a new color spec somewhat close to the previous color + +procedure newcolor() + static r, g, b, c + + initial { + randomize() + r := ?32767 + g := ?32767 + b := ?32767 + c := integer(32767 - 32767 * opttab["c"]) + c <:= 1 + } + + r +:= ?c - c/2 - 1; r <:= 0; r >:= 32767 + g +:= ?c - c/2 - 1; g <:= 0; g >:= 32767 + b +:= ?c - c/2 - 1; b <:= 0; b >:= 32767 + return (r + 32768) || "," || (g + 32768) || "," || (b + 32768) +end diff --git a/ipl/gprogs/etch.icn b/ipl/gprogs/etch.icn new file mode 100644 index 0000000..d491554 --- /dev/null +++ b/ipl/gprogs/etch.icn @@ -0,0 +1,153 @@ +############################################################################ +# +# File: etch.icn +# +# Subject: Program for distributed Etch-A-Sketch +# +# Author: Clinton L. Jeffery +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# A drawing program. Invoked with one optional argument, the +# name of a remote host on which to share the drawing surface. +# +# Dragging the left button draws black dots +# The middle button draws a line from button press to the release point +# The right button draws white dots +# Control-L clears the screen +# The Escape character exits the program +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen, xcompat +# +############################################################################ + +link wopen +link xcompat + +procedure main(av) + local w1, w2, w3, w4, w5, w6, w, x1, xa, x2, xb, y1, ya, y2, yb, dragging, + da, xc, xd, yc, yd, dc, e + # + # open an etch window. If there was a command line argument, + # attempt to open a second window on another display. For + # each window, create a binding with reverse video for erasing. + # + w1 := WOpen("label=etch", "size=300,300") | stop("can't open window") + w2 := XBind(w1,"drawop=xor") | stop("can't XBind w2") + w3 := XBind(w1,"reverse=on") | stop("can't XBind w3") + if *av>0 then { + w4 := WOpen("label=etch", "display="||av[1]||":0","size=300,300") | + stop("can't open window, display=",av[1]) + w5 := XBind(w4,"drawop=xor") | stop("Can't XBind w5") + w6 := XBind(w4,"reverse=on") | stop("Can't XBind w6") + } + repeat { + # + # wait for an available event on either display + # + w := Active() | stop("Active fails") + if (w === (w1|w2)) then { + x1 := xa + x2 := xb + y1 := ya + y2 := yb + dragging := da + } else { + x1 := xc + x2 := xd + y1 := yc + y2 := yd + dragging := dc + } + + case e := Event(w) of { + # + # Mouse down events specify an (x1,y1) point for later drawing. + # (x2,y2) is set to null; each down event starts a new draw command. + # + &lpress | &mpress | &rpress: { + x1 := &x + y1 := &y + x2 := y2 := &null + } + # + # Mouse up events obtain second point (x2,y2), and draw a line. + # + &lrelease: { + DrawLine(w1,\x1,\y1,&x,&y) + DrawLine(\w4,\x1,\y1,&x,&y) + } + &mrelease: { + DrawLine(w1,x1,y1,&x,&y) + DrawLine(\w4,x1,y1,&x,&y) + dragging := &null + } + &rrelease: { + DrawLine(w3,x1,y1,&x,&y) + DrawLine(\w6,x1,y1,&x,&y) + } + # + # Drag events obtain a second point, (x2,y2), and draw a line + # If we are drawing points, we update (x1,y1); if we are + # drawing lines, we erase the "rubberband" line and draw a new + # one at each drag event; a permanent line will be drawn when + # the button comes up. + # + &ldrag : { + DrawLine(w1,x1,y1,&x,&y) + DrawLine(\w4,x1,y1,&x,&y) + # left and right buttons use current position + x1 := &x # for subsequent operations + y1 := &y + } + &rdrag : { + DrawLine(w3,x1,y1,&x,&y) + DrawLine(\w6,x1,y1,&x,&y) + # left and right buttons use current position + x1 := &x # for subsequent operations + y1 := &y + } + &mdrag: { + if /dragging then dragging := 1 + else { # erase previous line, if any + DrawLine(w2,x1,y1,\x2,\y2) + DrawLine(\w5,x1,y1,\x2,\y2) + } + x2 := &x + y2 := &y + DrawLine(w2,x1,y1,x2,y2) + DrawLine(\w5,x1,y1,x2,y2) + } + "\^l": { + EraseArea(w1) + EraseArea(\w4) + } + "\e": break + } + if (w === (w1|w2)) then { + xa := x1 + xb := x2 + ya := y1 + yb := y2 + da := dragging + } else { + xc := x1 + xd := x2 + yc := y1 + yd := y2 + dc := dragging + } + } +end diff --git a/ipl/gprogs/facebend.icn b/ipl/gprogs/facebend.icn new file mode 100644 index 0000000..9551847 --- /dev/null +++ b/ipl/gprogs/facebend.icn @@ -0,0 +1,792 @@ +############################################################################ +# +# File: facebend.icn +# +# Subject: Program to generate caricatures +# +# Author: Gregg M. Townsend +# +# Date: October 7, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Facebender is a caricature generator. Read in an image and use the +# left mouse button to pick the key points as prompted. Click the +# right button to skip a feature. Pull down "drawing" on the display +# menu to see the caricature. Move the slider to change the distortion. +# +############################################################################ +# +# References: +# +# A. K. Dewdney, "Computer Recreations". Scientific American, Oct. 1986. +# Reprinted in two collections of his columns, both from W. H. Freeman: +# The Armchair Universe (1988) and The Tinkertoy Computer (1993). +# +# Susan E. Brennan, "Caricature Generator: The Dynamic Exaggeration of +# Faces by Computer." Leonardo, Vol.18 no.3, 1985, pp. 170-178. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, vsetup +# +############################################################################ + + +link graphics # graphics library +link vsetup # VIB library + + +# constant definitions + +$define PupilRadius 2 # radius for drawing pupils of eyes + +$define TargetRad1 5 # radii for guide display target +$define TargetRad2 20 + +$define ImageMode 1 # drawing modes +$define DrawMode 2 +$define DualMode 3 + + +# vidgets and geometry + +global vidgets # vidget table + +global display_xoff, display_yoff # image area +global display_width, display_height +global image_xoff, image_yoff # centered image + +global guide_xoff, guide_yoff # guide area +global guide_width, guide_height + +global prompt_xoff, prompt_yoff # prompt area +global prompt_width, prompt_height + +global dmeter_xoff, dmeter_yoff # distortion meter +global dmeter_width, dmeter_height + +# windows and bindings + +global image_win # scanned image +global target_win # binding for point targets +global display_win # binding for image or caricature +global overlay_win # binding for dual-mode display + +# face data +# +# (A face is a list of curves, beginning with the left and right pupils; +# a curve is a list of x and y coordinates.) + +global descriptions # labels for facial curves + +global stdface # standard (average) face +global guideface # scaled / translated guide face +global sketch # points from subject face + +global tcurve # index of current curve to place +global tpoint # index of point within curve + +# miscellaneous globals + +global pointfile # file name for saving coordinates +global touched # has data changed since last save? + +global mode # Image / Draw / Dual mode +global distortion # distortion factor (0.0 = undistorted) + + +# main program + +procedure main() + local l, r, y + + # Open the window, extract layout information, initialize dialogs. + + vidgets := ui() + WAttrib("pointer=circle") # may fail, but at least try + init_geometry() + + # Make two clipped bindings for displaying the image and sketch. + + display_win := Clone("linewidth=2") + Clip(display_win, display_xoff, display_yoff, display_width, display_height) + overlay_win := Clone(display_win, "fillstyle=masked", "pattern=4,#9696") + + # Make a clipped binding for displaying targets on the guide display. + + target_win := Clone("drawop=reverse") + Clip(target_win, guide_xoff, guide_yoff, guide_width, guide_height) + + # Initialize globals. + + init_stdface() # coordinates of "standard" face + mode := ImageMode # display mode + setdist(0) # distortion factor + + # Use the standard face to create a guide display for locating targets. + # Calculate eye locations to use for scaling; then draw the face + # with straight lines to emphasize the individual point locations. + + l := guide_xoff + 3 * guide_width / 8 + r := guide_xoff + 5 * guide_width / 8 + y := guide_yoff + guide_height / 2 + guideface := scaleface(stdface, [[l, y], [r, y]]) + drawface(&window, guideface, DrawLine) + + # Load and display an image; exit if dialog is cancelled. + + new() | exit() + + # Enter event loop. + + GetEvents(vidgets["root"], , shortcuts) + +end + + +# caricature() -- draw sketch distorted by current distortion factor + +procedure caricature() + local base, face, win + + if /sketch | /sketch[1, 1] | /sketch[2, 1] then + fail # must have both pupils to draw + + if mode = DrawMode then + win := display_win # use all the display area pixels + else + win := overlay_win # use subpattern of display pixels + + Fg(win, "white") + FillRectangle(win) # clear clipped area using fillstyle + Fg(win, "black") + + base := scaleface(stdface, sketch) + face := distort(sketch, base, distortion) + drawface(win, face, DrawCurve) # draw distorted face + + return + +end + + +# check_save() -- check to see if previous coordinate needs to be saved +# +# check_save fails if cancelled. + +procedure check_save() + + if \touched then + case SaveDialog("Save coordinates first?", pointfile) of { + "Yes": { + pointfile := dialog_value + save() | save_as() | fail + } + "No": return + "Cancel": fail + } + + return + +end + + +# distort(f, b, m) -- return distortion of face f from face b by factor m + +procedure distort(f, b, m) + local r, t, i, j, curve, base + + r := [] + every i := 1 to *f do { + base := b[i] + put(r, curve := copy(f[i])) + if /curve[-1] | /base[-1] then + next # incomplete placeholder + every j := 1 to *curve by 2 do { + curve[j] +:= m * (curve[j] - base[j]) + curve[j + 1] +:= m * (curve[j + 1] - base[j + 1]) + } + } + + return r + +end + + +# drawface(win, f, proc) -- draw face from curve list using proc + +procedure drawface(win, f, proc) + local curve + + every curve := copy(!f) do { + if /curve[-1] then # null coordinate + next # incomplete curve + if *curve = 2 then + FillCircle(win, curve[1], curve[2], PupilRadius) + else { + push(curve, win) + proc ! curve + } + } + + return + +end + + +# init_geometry() -- extract layout information from vidgets + +procedure init_geometry() + + guide_xoff := vidgets["guide"].ax + guide_yoff := vidgets["guide"].ay + guide_width := vidgets["guide"].aw + guide_height := vidgets["guide"].ah + + display_xoff := vidgets["image"].ax + display_yoff := vidgets["image"].ay + display_width := vidgets["image"].aw + display_height := vidgets["image"].ah + + prompt_xoff := vidgets["prompt"].ax + prompt_yoff := vidgets["prompt"].ay + prompt_width := vidgets["prompt"].aw + prompt_height := vidgets["prompt"].ah + + dmeter_xoff := vidgets["dmeter"].ax + dmeter_yoff := vidgets["dmeter"].ay + dmeter_width := vidgets["dmeter"].aw + dmeter_height := vidgets["dmeter"].ah + + return + +end + + +# init_stdface() -- initialize standard face and description list + +procedure init_stdface() + local spec + + descriptions := [] + stdface := [] + every spec := ![ + ["left pupil",145,203], # must be first + ["right pupil",255,203], # must be second + ["top of left eyebrow",101,187,105,177,126,168,153,170,177,176,181,185], + ["top of right eyebrow",219,185,223,176,247,170,274,168,295,177,299,187], + ["bottom of left eyebrow",102,188,124,177,151,181,181,185], + ["bottom of right eyebrow",219,185,249,181,276,177,298,188], + ["top of left eye",114,199,141,187,172,198], + ["top of right eye",228,198,259,187,286,199], + ["bottom of left eyelid",116,207,143,194,170,206], + ["bottom of right eyelid",230,206,257,194,284,207], + ["bottom of left eye",120,208,142,213,170,206], + ["bottom of right eye",230,206,258,213,280,208], + ["left iris",144,195,132,201,144,211,156,201,145,195], + ["right iris",255,195,244,201,256,211,268,201,256,195], + ["left side of nose",190,193,190,219,190,244,186,257,189,271,200,277], + ["right side of nose",210,193,210,219,210,244,214,257,211,271,200,277], + ["left nostril",177,250,171,258,169,269,174,277,183,271,198,277], + ["right nostril",223,250,229,258,231,269,226,277,217,271,202,277], + ["top of upper lip",152,318,172,311,188,306,200,311,212,306, + 228,311,248,318], + ["bottom of upper lip",152,318,170,319,186,317,200,319,214,317, + 230,319,248,318], + ["top of lower lip",152,318,172,318,186,317,200,319,214,317, + 228,318,248,318], + ["bottom of lower lip",152,318,169,327,184,333,200,335,216,333, + 231,327,248,318], + ["left ear",75,212,61,201,54,213,58,233,64,260,75,285,85,281], + ["right ear",325,212,339,201,346,213,342,233,336,260,325,285,315,281], + ["top of head",60,317,28,254,31,189,46,108,82,47,141,4,200,1,259,4, + 318,47,354,108,369,189,372,254,340,317], + ["hairline",79,200,90,168,104,141,119,120,143,104,172,100,200,99, + 228,100,257,104,281,120,296,141,310,168,321,200], + ["left side of face",84,194,79,232,86,273], + ["right side of face",316,194,321,232,314,273], + ["jaw",85,272,93,311,108,342,133,369,167,392,200,399,233,392, + 267,369,292,342,307,311,315,272], + ["left eye line",131,221,148,220,166,214], + ["right eye line",234,214,252,220,269,221], + ["left cheek line",167,264,154,278,145,294], + ["right cheek line",233,264,246,278,255,294], + ["left cheekbone",87,269,95,280,101,292], + ["right cheekbone",313,269,305,280,299,292], + ["chin cleft",200,377,200,389], + ["chin line",180,350,200,345,220,350] + ] do { + put(descriptions, get(spec)) + put(stdface, spec) + } + + return + +end + + +# load() -- load coordinate data + +procedure load() + local input, face + + check_save() | fail + repeat { + case OpenDialog("Load coordinates:") of { + "Okay": { + if input := open(dialog_value) then break else + Notice("Can't open " || dialog_value) + } + "Cancel": fail + } + } + + if sketch := rdface(input) then { + close(input) + pointfile := dialog_value + touched := &null + if mode ~= ImageMode then + redisplay() + target(1, 1) + return + } + + else { + Notice("Not a valid coordinate file") + close(input) + fail + } + +end + + +# menu_cb() -- handle menu selections + +procedure menu_cb(vidget, menu) + + case menu[1] of { + + "load @L": load() + "new @N": new() + "save @S": save() + "save as ": save_as() + "quit @Q": quit() + + "image @I": { + mode := ImageMode + redisplay() + } + "drawing @D": { + mode := DrawMode + redisplay() + } + "both @B": { + mode := DualMode + redisplay() + } + } + + return + +end + + +# new() -- load new image + +procedure new() + local input, f + + check_save() | fail + repeat { + case OpenDialog("Load image:") of { + "Okay": { + if rdimage(dialog_value) then + return + if f := open(dialog_value) then { + close(f) + Notice(dialog_value || " is not a valid image") + } + else + Notice("Can't open " || dialog_value) + } + "Cancel": fail + } + } + +end + + +# point_cb() -- handle event in display region + +procedure point_cb(vidget, e) + + if /tcurve then # if no points are left unset + return + + case e of { + + &lrelease: { # left button sets current point + sketch[tcurve, 2 * tpoint - 1] := &x + sketch[tcurve, 2 * tpoint] := &y + touched := 1 + if mode ~= ImageMode & *sketch[tcurve] = 2 * tpoint then + redisplay() # redraw if new curve done + target(tcurve, tpoint) # update target display + } + + &rrelease: { # right button skips a curve + every !sketch[tcurve] := &null # clear all points on curve + if (tcurve +:= 1) > *sketch then + tcurve := 1 + target(tcurve, 1) # set target to next curve + } + + } + + return + +end + + +# quit() -- terminate session + +procedure quit() + + check_save() | fail + exit() + +end + + +# rdface(f) -- read face coordinates from file f + +procedure rdface(f) + local face, line, curve, i, n + + face := [] + while line := read(f) do line ? { + =":" | next # ignore line missing ":" + curve := [] + + while tab(upto(&digits)) do { + n := integer(tab(many(&digits))) + if n ~= 0 then n +:= image_xoff else n := &null + put(curve, n) + + tab(upto(&digits)) | break + n := integer(tab(many(&digits))) + if n ~= 0 then n +:= image_yoff else n := &null + put(curve, n) + } + + put(face, curve) + } + + # Validate the number of curves and points. + + if *face ~= *stdface then fail + every i := 1 to *stdface do + if *face[i] ~= *stdface[i] then fail + + return face + +end + + +# rdimage(filename) -- load image from file, failing if unsuccessful + +procedure rdimage(filename) + local curve + + image_win := WOpen("image=" || filename, "canvas=hidden") | fail + pointfile := &null + touched := &null + + # Calculate offsets that center the image in display area. + + image_xoff := display_xoff + + (display_width - WAttrib(image_win, "width")) / 2 + image_yoff := display_yoff + + (display_height - WAttrib(image_win, "height")) / 2 + + # Initialize a new set of (unset) points. + + sketch := [] + every curve := !stdface do + put(sketch, list(*curve, &null)) + target(1, 1) # reset to start with first point + + # Ensure that current mode includes the image, and update the display. + + if mode = DrawMode then + mode := ImageMode + EraseArea(display_xoff, display_yoff, display_width, display_height) + redisplay() + + return + +end + + +# redisplay() -- display image and/or drawing, depending on mode + +procedure redisplay() + + if mode ~= DrawMode then + CopyArea(image_win, display_win, , , , , image_xoff, image_yoff) + if mode ~= ImageMode then + caricature() + + return + +end + + +# save() -- save coordinate data + +procedure save() + local output + + if /pointfile then + return save_as() + + output := open(pointfile, "w") | { + Notice("Can't write " || pointfile) + fail + } + wtface(output, sketch) + close(output) + touched := &null + + return + +end + + +# save_as() -- save coordinate data in alternate file + +procedure save_as() + local output + + repeat { + case SaveDialog("Save coordinates?", "") of { + "No": return + "Cancel": fail + "Yes": + if output := open(dialog_value, "w") then break else + Notice("Can't write " || dialog_value) + } + } + + wtface(output, sketch) + close(output) + pointfile := dialog_value + touched := &null + + return + +end + + +# scaleface(f, g) -- return copy of face f scaled to overlay face g + +procedure scaleface(f, g) + local fl, fr, gl, gr, fx, fy, gx, gy, m, r, t, curve + + fl := f[1] | fail # left iris + fr := f[2] | fail # right iris + gl := g[1] | fail # target left iris + gr := g[2] | fail # target right iris + fx := (fl[1] + fr[1]) / 2.0 # x offset of f + fy := (fl[2] + fr[2]) / 2.0 # y offset of f + gx := (gl[1] + gr[1]) / 2.0 # x offset of g + gy := (gl[2] + gr[2]) / 2.0 # y offset of g + m := (gr[1] - gl[1]) / real(fr[1] - fl[1]) + # multiplier + + r := [] + every curve := copy(!f) do { + if /curve[-1] then + put(r, curve) # incomplete placeholder + else { + put(r, t := []) + while put(t, m * (get(curve) - fx) + gx) do + put(t, m * (get(curve) - fy) + gy) + } + } + + return r + +end + + +# setdist(val) -- set and display distortion value, in percent + +procedure setdist(val) + + distortion := val / 100.0 + GotoXY(dmeter_xoff, dmeter_yoff + dmeter_height) + WWrites(right(integer(val), 4), "%") + + return + +end + + +# shortcuts() -- check event for keyboard shortcut + +procedure shortcuts(e) + + if &meta then case map(e) of { + "l": load() + "n": new() + "s": save() + "q": quit() + "i": { + mode := ImageMode + redisplay() + } + "d": { + mode := DrawMode + redisplay() + } + "b": { + mode := DualMode + redisplay() + } + } + + return + +end + + +# slider_cb() -- handle adjustments of distortion slider + +procedure slider_cb(vidget, val) + + setdist(val) # update and display value + if mode = ImageMode then # ensure that mode includes drawing + mode := DualMode + redisplay() # draw updated sketch + + return + +end + + +# target(curve, point) -- display next point to be placed + +procedure target(curve, point) + local s, n, x, y + static tx, ty + + # Undraw the previous target and erase the previous prompt. + + FillCircle(target_win, \tx, \ty, TargetRad1) + FillCircle(target_win, \tx, \ty, TargetRad2) + EraseArea(prompt_xoff, prompt_yoff, prompt_width, prompt_height) + + # Start from specified place unless the pupils remain unplaced. + + if \sketch[1, 1] & \sketch[2, 1] then { + tcurve := curve + tpoint := point + } + else { + tcurve := 1 + tpoint := 1 + } + + # Find the next unset point. + + until /sketch[tcurve, 2 * tpoint - 1] do { + tpoint +:= 1 # advance to next point + if tpoint > (2 * *guideface[tcurve]) then { + tpoint := 1 # need to move to next curve + tcurve +:= 1 + } + if tcurve > *guideface then + tcurve := 1 # wrapped around list of curves + if tcurve = curve & tpoint = point then { + tcurve := tx := ty := &null # there are no unset points + return + } + } + + # Draw a target on the guide face. + + tx := guideface[tcurve, 2 * tpoint - 1] + ty := guideface[tcurve, 2 * tpoint] + FillCircle(target_win, tx, ty, TargetRad1) + FillCircle(target_win, tx, ty, TargetRad2) + + # Display the prompt. + + x := prompt_xoff + prompt_width / 2 + y := prompt_yoff + prompt_height / 2 + s := "locate " || descriptions[tcurve] + n := *guideface[tcurve] + if n > 2 then + s ||:= " (select " || n / 2 || " points)" + CenterString(x, y, s) + + return + +end + + +# wtface(f, face) -- write face data to file f + +procedure wtface(f, face) + local curve, i + + every curve := !face do { + writes(f, ":") + every i := 1 to *curve by 2 do { + writes(f, " ", (\curve[i] - image_xoff) | 0) + writes(f, " ", (\curve[i + 1] - image_yoff) | 0) + } + write(f) + } + + return + +end + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=640,480", "bg=pale gray", "label=Caricaturist"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,640,480:Caricaturist",], + ["distort:Slider:h:1:10,436,230,22:-300,300,0",slider_cb], + ["dmenu:Menu:pull::36,0,57,21:Display",menu_cb, + ["image @I","drawing @D","both @B"]], + ["fmenu:Menu:pull::0,0,36,21:File",menu_cb, + ["new @N","load @L","save @S","save as ","quit @Q"]], + ["header_line:Line:::0,22,639,22:",], + ["label1:Label:::11,409,77,13:distortion:",], + ["label2:Label:::9,460,28,13:anti",], + ["label3:Label:::104,460,42,13:normal",], + ["label4:Label:::213,460,28,13:wild",], + ["vert_line:Line:::250,23,250,479:",], + ["dmeter:Rect:invisible::104,410,41,10:",], + ["prompt:Rect:invisible::252,1,387,19:",], + ["guide:Rect:invisible::1,24,247,280:",], + ["image:Rect:invisible::252,24,387,455:",point_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/fetti.icn b/ipl/gprogs/fetti.icn new file mode 100644 index 0000000..b872f12 --- /dev/null +++ b/ipl/gprogs/fetti.icn @@ -0,0 +1,202 @@ +############################################################################ +# +# File: fetti.icn +# +# Subject: Program to explore families of confetti squares +# +# Author: Gregg M. Townsend +# +# Date: November 12, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Fetti is an interactive program for generating decorative +# web-page sidebars composed of randomly colored squares. Many +# different parameters can be varied on the control panel. Note +# that the mouse must be over a numeric field to type in a new +# value. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: vsetup +# +############################################################################ + +link vsetup + +global vidgets, root, region, rwin + +procedure main(args) + + Window ! put(ui_atts(), args) + vidgets := ui() # set up vidgets + root := vidgets["root"] + region := vidgets["region"] + rwin := SubWindow(region.ax, region.ay, region.aw, region.ah) + Bg(rwin, "white") + + render() + GetEvents(root, , all) +end + +procedure all(a, x, y) + if a === !" \n\r" then render() + else if &meta then case a of { + !"qQ": exit() + !"sS": save() + } + return +end + +procedure huebutton(v, x) + case v.id of { + "r": huerange(0, 15) + "o": huerange(15, 45) + "y": huerange(45, 60) + "g": huerange(90, 150) + "c": huerange(165, 195) + "b": huerange(195, 225) + "p": huerange(255, 285) + "m": huerange(285, 315) + "all": huerange(0, 360) + "ygb": huerange(45, 195) + "bmr": huerange(195,360) + } +end + +procedure huerange(min, max) + txtval("hmin", min, min) + txtval("hmax", max, max) + render() +end + +procedure render() + local side, gap, across, down + local hmin, hmax, smin, smax, vmin, vmax + local i, j, h, s, v, color ,clist + + side := txtval("side", 1, 100) + gap := txtval("gap", 0, 100) + across := txtval("across", 1, 1000) + down := txtval("down", 1, 1000) + hmin := txtval("hmin", 0, 360) + hmax := txtval("hmax", hmin, 360) + smin := txtval("smin", 0, 100) + smax := txtval("smax", smin, 100) + vmin := txtval("vmin", 0, 100) + vmax := txtval("vmax", vmin, 100) + + EraseArea() # for color recycling + VDraw(root) # needed after erase + + EraseArea(rwin) + clist := [] + every i := 0 to down - 1 do { + every j := 0 to across - 1 do { + h := hmin + integer(?(hmax - hmin)) + s := smin + integer(?(smax - smin)) + v := vmin + integer(?(vmax - vmin)) + color := HSVValue(h || "/" || s || "/" || v) + if Fg(rwin, color) then + put(clist, color) + else + Fg(rwin, ?clist) + FillRectangle(rwin, + gap + j * (gap + side), gap + i * (gap + side), side, side) + } + } + + return +end + +procedure txtval(s, min, max) + local v, n + + v := vidgets[s] + VEvent(v, "\r", v.ax, v.ay) + n := integer(VGetState(v)) | min + n <:= min + n >:= max + VSetState(v, n) + return n +end + +procedure save() + local g + + g := WAttrib("gamma") + WAttrib("gamma=1.0") # don't gamma-correct on write + repeat case OpenDialog("Save confetti image:") of { + "Cancel": { + WAttrib("gamma=" || g) + fail + } + "Okay": { + if WriteImage(dialog_value, region.ax, region.ay, region.aw, region.ah) + then + break + else + Notice("cannot write file:", dialog_value) + } + } + WAttrib("gamma=" || g) + return +end + +procedure quit() + exit() +end + + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=400,500", "bg=pale gray", "label=fetti"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,400,500:fetti",], + ["across:Text::3:300,59,87,19:Across: \\=5",], + ["all:Button:regular::259,235,28,17:all",huebutton], + ["b:Button:regular::315,218,14,17:b",huebutton], + ["bmr:Button:regular::315,235,28,17:bmr",huebutton], + ["c:Button:regular::301,218,14,17:c",huebutton], + ["down:Text::3:300,82,87,19:Down: \\=50",], + ["g:Button:regular::287,218,14,17:g",huebutton], + ["gap:Text::3:213,82,73,19:Gap: \\=1",], + ["hlab:Label:::267,126,21,13:Hue",], + ["hmax:Text::3:261,167,31,19:\\=360",], + ["hmin:Text::3:261,144,31,19:\\=0",], + ["m:Button:regular::343,218,14,17:m",huebutton], + ["malab:Label:::216,167,21,13:max",], + ["mnlab:Label:::216,144,21,13:min",], + ["o:Button:regular::259,218,14,17:o",huebutton], + ["p:Button:regular::329,218,14,17:p",huebutton], + ["quit:Button:regular::260,439,78,29:quit @Q",quit], + ["r:Button:regular::245,218,14,17:r",huebutton], + ["render:Button:regular::269,318,62,35:RENDER",render], + ["save:Button:regular::260,408,78,29:save @S",save], + ["side:Text::3:213,59,73,19:Side: \\=9",], + ["slab:Label:::314,126,21,13:Sat",], + ["smax:Text::3:308,167,31,19:\\=70",], + ["smin:Text::3:308,144,31,19:\\=20",], + ["title:Label:::250,21,98,13:Confetti Maker",], + ["vlab:Label:::361,126,21,13:Val",], + ["vmax:Text::3:355,167,31,19:\\=100",], + ["vmin:Text::3:355,144,31,19:\\=80",], + ["y:Button:regular::273,218,14,17:y",huebutton], + ["ygb:Button:regular::287,235,28,17:ygb",huebutton], + ["outline:Rect:sunken::261,309,78,52:",], + ["region:Rect:invisible::0,0,200,500:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/fev.icn b/ipl/gprogs/fev.icn new file mode 100644 index 0000000..01ea01f --- /dev/null +++ b/ipl/gprogs/fev.icn @@ -0,0 +1,170 @@ +############################################################################ +# +# File: fev.icn +# +# Subject: Program to display text in fisheye view +# +# Author: Clinton L. Jeffery +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# A text file browser that employs a fish-eye view. The +# fish-eye view displays text in a larger font in the middle (focus) +# gradually declining to tiny fonts at the top and bottom of the screen. +# +# "q" or ESC to quit. "n" slides the focus down one line +# "p" slides the focus up one line. "w" widens the focus by one. +# "W" narrows the focus by one. Mouse clicks move the focus to the line +# on which the mouse is located; clicking in the left margin moves +# in the file proportional to the mouse's y coordinate in the window. +# +############################################################################ +# +# Requires: Version 9 graphics with X11R5 scalable fonts on the X server +# +############################################################################ +# +# Links: wopen, xcompat +# +############################################################################ + +link wopen +link xcompat + +procedure main(av) + local slope, fin, win, L, ht, focus, focuswidth, e, base + slope := 2 + if av[1] == "-s" then slope := (pop(av), pop(av)) + fin := open(av[1]) | stop("no file") + win := WOpen("label=" || av[1], "height=860") | stop("no window") + L := [] + every put(L,!fin) + write(*L," lines") + ht := 23 + focus := *L/2 + focuswidth := 1 + fisheye(win,L,focus,ht,slope,,,focuswidth) + repeat { + e := Event(win) + case e of { + "q"|"\e": exit(0) + "n" : focus := *L >= focus+1 + "p" : focus := 1 <= focus - 1 + "w" : focuswidth +:= 1 + "W" : (1 < focuswidth) -:= 1 + &lpress|&ldrag|&mpress|&mdrag|&rpress|&rdrag: { + if &x < 17 then { + focus := *L * &y / WAttrib(win,"height") + } + else { + base := WAttrib(win,"height") / 2 + focus := moveFocusToMouse(win,L,focus,base,ht,slope,focuswidth) + } + } + default : next + } + fisheye(win,L,focus,ht,slope,,,focuswidth) + } +end + +procedure fisheye(w,L,focus,maxht,slope,family,weight,focuswidth) + static fonttable + local past_end, i, splt + initial { + fonttable := table() + } + /focuswidth := 1 + /family := "helvetica" + /weight := "bold" + + /fonttable[w] := [] + + past_end := *fonttable[w] + 1 + every i := past_end to maxht do { + put(fonttable[w], + XBind(w,"font=-adobe-"||family||"-"||weight|| + "-r-normal--"||i||"-*-*-*-*-*-*-*") | stop("no XBind")) + } + EraseArea(w) + splt := WAttrib(w,"height") / 2 + viewtop(fonttable[w],L,focus,splt,maxht,slope,focuswidth) + viewbottom(fonttable[w],L,focus+1,splt+maxht,maxht-slope,slope,focuswidth) + FillRectangle(w,0,(focus * WAttrib(w,"height") / *L)-WAttrib(w,"ascent"), + 16,WAttrib(w,"fheight")) + DrawLine(w,17,0,17,WAttrib(w,"height") * focuswidth) + +end + +procedure viewtop(w,L,focus,base,ht,slope,focuswidth) + local wh + wh := WAttrib(w[1],"height") | stop("no WAttrib") + while focus >= 1 & base >= 1 do { + if ht < 1 then ht := 1 + GotoXY(w[1],20,base) + + writes(w[ht],L[focus]) + base -:= ht + if focus > 1 & base / focus < ht & focuswidth <= 1 then + ht -:= slope + focuswidth -:= 1 + focus -:= 1 + } + if focus < 1 then return 1 + return focus +end + +procedure viewbottom(w,L,focus,base,ht,slope,focuswidth) + local wh + wh := WAttrib(w[1],"height") | stop("no WAttrib") + while focus <= *L & base <= wh do { + if ht < 1 then ht := 1 + GotoXY(w[1],20,base) + + writes(w[ht],L[focus]) + base +:= ht + if focus < *L & (wh - base) / (*L - focus) < ht & focuswidth <= 1 then + ht -:= slope + focuswidth -:= 1 + focus +:= 1 + } + if focus > *L then return *L + return focus +end + +procedure moveFocusToMouse(w,L,focus,base,ht,slope,focuswidth) + local wh, fh + wh := WAttrib(w,"height") | stop("no WAttrib") + fh := WAttrib(w,"ascent") | stop("no WAttrib") + if &y < base then { + while focus >= 1 & base-fh >= &y do { + if ht < 1 then ht := 1 + base -:= ht + if focus > 1 & base / focus < ht & focuswidth <= 1 then + ht -:= slope + focuswidth -:= 1 + focus -:= 1 + } + } + else { + focus +:= 1 + base +:= ht + ht -:= slope + while focus <= *L & base <= &y do { + if ht < 1 then ht := 1 + base +:= ht + if focus < *L & (wh - base) / (*L - focus) < ht & focuswidth <= 1 then + ht -:= slope + focuswidth -:= 1 + focus +:= 1 + } + } + if focus < 1 then return 1 + if focus > *L then return *L + return focus +end diff --git a/ipl/gprogs/fileimag.icn b/ipl/gprogs/fileimag.icn new file mode 100644 index 0000000..1c46190 --- /dev/null +++ b/ipl/gprogs/fileimag.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: fileimag.icn +# +# Subject: Program to create GIF image of file text +# +# Author: Ralph E. Griswold +# +# Date: July 8, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program creates an image file for a text file. The results are +# unpredictable for binary files or files with control characters. +# +# The image may be too large for a window. +# +# Badly needed are options for the font. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(args) + local input, width, height, line + + input := open(args[1]) | stop("*** cannot open file") + + width := height := 0 + + while line := read(input) do { + height +:= 1 + width <:= *line + } + + height +:= 1 + + close(input) + + input := open(args[1]) | stop("*** cannot re-open file") + + WOpen("canvas=hidden", "columns=" || width, "lines=" || height) | + stop("*** cannot open window") + + while WWrite(WRead(input)) + + WriteImage("untitled.gif") + + +end diff --git a/ipl/gprogs/findrpt.icn b/ipl/gprogs/findrpt.icn new file mode 100644 index 0000000..de9a6a2 --- /dev/null +++ b/ipl/gprogs/findrpt.icn @@ -0,0 +1,100 @@ +############################################################################ +# +# File: findrpt.icn +# +# Subject: Program to find smallest repeat in a repeat pattern +# +# Author: Ralph E. Griswold +# +# Date: December 5, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces the smallest motif in an image that will tile +# to the image. +# +# The image to be processed must be a "true" repeat -- pixel for pixel. +# +# The options supported are: +# +# -n s suffix for output image, default _t. The suffix is +# appended to the basename of the input image, as in +# foo.gif -> foo_t.gif. +# +# -s show size; default produce image +# +# Warning: This program is *very* slow. +# +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: numbers, options, repetit, wopen +# +############################################################################ + +link numbers +link options +link repetit +link wopen + +procedure main(args) + local width, height, x, y, row, col, rows, cols, w, h, suffix, file + local basename, opts + + opts := options(args, "n:s") + suffix := \opts["s"] | "_t" + + every file := !args do { + WOpen("canvas=hidden", "image=" || file) | { + write(&errout, "*** cannot open ", file) + next + } + file ? { + basename := 1(tab(find(".gif")), move(0)) | "unname" + } + width := WAttrib("width") + height := WAttrib("height") + + rows := [] + every y := 0 to height - 1 do { + row := [] + every put(row,Pixel(0, y, width, 1)) + put(rows, repetit(row)) + } + h := lcml ! rows + h >:= height + + cols := [] + every x := 0 to width - 1 do { + col := [] + every put(col, Pixel(x, 0, 1, height)) + put(cols, repetit(col)) + } + w := lcml ! cols + w >:= width + + if w = width & h = height then { + write(&errout, file, " has no subrepeat") + next + } + + if \opts["s"] then + write(file, ": ", w, "x", h) + else + WriteImage(basename || suffix || ".gif", 0, 0, w, h) | { + write(&errout, "*** cannot write image for ", file) + write(&errout, "w=", w, " h=", h) + } + WClose(&window) + &window := &null + } + +end diff --git a/ipl/gprogs/findtile.icn b/ipl/gprogs/findtile.icn new file mode 100644 index 0000000..0efc926 --- /dev/null +++ b/ipl/gprogs/findtile.icn @@ -0,0 +1,599 @@ +############################################################################ +# +# File: findtile.icn +# +# Subject: Program to find tiles in an image +# +# Author: Ralph E. Griswold +# +# Date: January 7, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program is designed to assist in locating areas within an image +# that, when tiled, produce a desired effect. For example, a background +# may consist of a tiled image; this program can be used to find the +# smallest tile for the repeat (by "eye-balling"). It's worth noting +# that interesting images can be found for other settings. For example, +# another interesting use of this program is to produce striped patterns by +# selecting a row or column of an image to get a tile that is one character +# wide. Sometimes a few rows or columns give an interesting "fabric" +# effect. +# +# There are three windows: +# +# the VIB control window +# the source image window +# a repeat window, which shows the selection from the source +# image, tiled. +# +# The selection from the source image is shown as a marquee in the +# source image window. When a source image is loaded, the marquee starts +# with the entire image. The marquee can be changed by buttons and +# arrow-key events on the control window (not the source image window). +# +# The arrow keys have two modes. With no modifier, they nudge the +# location of the marquee. With the meta-key modifier, they nudge +# the dimensions of the marquee. +# +# The reset button resets the marquee to the entire image. +# +# The current selection can be mirrored using the mirror button. +# +# The following features are provided through keyboard shortcuts: +# the File menu, and in some cases, on-board buttons: +# +# @O open new source image +# @Q quit application +# @S save current selection as an image +# @Z set size precisely +# +# The repeat window can be resized by the user, but it is not redrawn +# until the marque is changed or the refresh button is pushed. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: grecords, interact, mirror, tile +# +############################################################################ +# +# Includes: keysyms.icn +# +############################################################################ + +link grecords +link interact +link mirror +link tile + +$include "keysyms.icn" + +# Globals related to windows: + +global controls # VIB control window +global pattern # repeat window +global screen # source image window visible +global source # source image window hidden +global symmetry # mirroring window + +global posx # x position relative to interface window +global posy # y position relative to repeat window + +# Globals related to the selection: + +global current # current selection record +global hmax # maximum height of source image +global wmax # maximum width of source image +global previous # previous selection record + +global vidgets # table of interface vidgets + +procedure main() + local atts, x1, y1 + + atts := ui_atts() + put(atts, "posx=10", "posy=10") + + controls := (WOpen ! atts) | ExitNotice("Cannot open control window.") + + vidgets := ui() + + init() + + repeat { + while *Pending(controls) > 0 do + ProcessEvent(vidgets["root"], , shortcuts) + while *Pending(\screen) > 0 do + if Event(screen) === &lpress then draw_marquee() + } + +end + +# Callback that handles all the buttons that change x, y, w, and h. + +procedure adjust_cb(vidget, value) + + check_source() | fail + + # Cute code alert: The selected reversible assignment is performed + # and passed to check(). It checks the resulting selection rectangle + # and fails if it's not valid. That failure causes the reversible + # assignment to be undone and the expression fails, leaving the + # selection as it was. + + case value[1] of { + "w max" : current.w := (wmax - current.x) + "h max" : current.h := (hmax - current.y) + "w = 1" : current.w := 1 + "h = 1" : current.h := 1 + "full" : { + current.h := hmax + current.w := wmax + current.x := 0 + current.y := 0 + } + "w / 2" : check(current.w <- current.w / 2) + "h / 2" : check(current.h <- current.h / 2) + "w * 2" : check(current.w <- current.w * 2) + "h * 2" : check(current.h <- current.h * 2) + } + + show() + + return + +end + +procedure draw_marquee() + local x1, y1 + + current.x := &x + current.y := &y + current.h := current.w := 0 + + update() + + repeat { + case Event(screen) of { + &ldrag : update_marquee() + &lrelease : { + update_marquee() + Raise(controls) + return + } + } + } + +end + +procedure update_marquee() + + if &x < 0 then &x := 0 + if &y < 0 then &y := 0 + if &x > wmax then &x := wmax + if &y > hmax then &y := hmax + current.w := &x - current.x + current.h := &y - current.y + + show() + + return + +end + +procedure location_cb(vidget, value) + + check_source() | fail + + # Cute code alert: The selected reversible assignment is performed + # and passed to check(). It checks the resulting selection rectangle + # and fails if it's not valid. That failure causes the reversible + # assignment to be undone and the expression fails, leaving the + # selection as it was. + + case value[1] of { + "nw" : current.x := current.y := 0 + "ne" : { + current.x := wmax - current.w + current.y := 0 + } + "se" : { + current.x := wmax - current.w + current.y := hmax - current.h + } + "sw" : { + current.x := 0 + current.y := hmax - current.h + } + "x max" : current.x := wmax - current.w + "y max" : current.y := hmax - current.h + "center" : { + current.x := (wmax - current.w) / 2 + current.y := (hmax - current.h) / 2 + } + "home" : { + current.x := 0 + current.y := 0 + } + "x / 2" : current.x <- current.x / 2 + "y / 2" : current.y <- current.y / 2 + "x * 2" : check(current.x <- current.x * 2) + "y * 2" : check(current.y <- current.y * 2) + } + + show() + + return + +end + +# Check validity of selection. + +procedure check() + + if (0 <= current.w <= (wmax - current.x)) & + (0 <= current.h <= (hmax - current.y)) & + (0 <= current.x <= hmax) & + (0 <= current.y <= wmax) + then return else { + Alert() + fail + } + +end + +# Copy hidden source window to a visible window. + +procedure copy_source(label) + + screen := WOpen( + "size=" || WAttrib(source, "width") || "," || WAttrib(source, "height"), + "posx=" || posx, + "posy=" || posy, + "label=" || label, + "drawop=reverse", + "linestyle=onoff" + ) | ExitNotice("Cannot open image window.") + + CopyArea(source, screen) + + Raise(controls) + + wmax := WAttrib(source, "width") + hmax := WAttrib(source, "height") + + WAttrib(pattern, "width=" || (WAttrib(screen, "width"))) + WAttrib(pattern, "height=" || (WAttrib(screen, "height"))) + EraseArea(pattern) + + current := rect(0, 0, wmax, hmax) + + show() + + return + +end + +# Handle file menu. + +procedure file_cb(vidget, value) + + case value[1] of { + "open @O" : get_image() + "quit @Q" : quit_cb() + "save @S" : save_cb() + } + + return + +end + +# Get new source image. + +procedure get_image() + + WClose(\source) + WClose(\screen) + WClose(\symmetry) + EraseArea(pattern) + + repeat { + (OpenDialog("Open image:") == "Okay") | fail + source := WOpen("canvas=hidden", "image=" || dialog_value) | { + Notice("Can't open " || dialog_value || ".") + next + } + copy_source(dialog_value) + wmax := WAttrib(source, "width") + hmax := WAttrib(source, "height") + break + } + + return + +end + +# These values are for Motif; they may need to be changed for other +# window managers. + +$define Offset1 32 +$define Offset2 82 + +# Initialize the program. + +procedure init() + local iheight + + posx := WAttrib(controls, "width") + Offset1 + + iheight := WAttrib(controls, "height") + + pattern := WOpen("label=repeat", "resize=on", "size=" || iheight || + "," || iheight, "posx=" || posx, "posy=10") | + ExitNotice("Cannot open pattern window.") + + posy := WAttrib(pattern, "height") + Offset2 + + Raise(controls) + + return + +end + +procedure update() + static sx, sy + + initial { + sx := vidgets["marker"].ax + sy := vidgets["marker"].ay + } + + # Update selection information on interface. + + WAttrib(controls, "drawop=reverse") + + DrawString(controls, sx, sy, "marquee: x=" || (\previous).x || " y=" || + previous.y || " w=" || previous.w || " h=" || previous.h) + DrawString(controls, sx, sy, "marquee: x=" || current.x || " y=" || + current.y || " w=" || current.w || " h=" || current.h) + + WAttrib(controls, "drawop=copy") + + # Update the selection rectangle. + + DrawRectangle(screen, (\previous).x, previous.y, previous.w, previous.h) + DrawRectangle(screen, current.x, current.y, current.w, current.h) + + previous := copy(current) + + return + +end + +procedure mirror_cb() + + check_source() | fail + + # Normalize selection rectangle. + + if current.w < 0 then { + current.w := -current.w + current.x -:= current.w + } + + if current.h < 0 then { + current.h := -current.h + current.y -:= current.h + } + + WClose(\symmetry) + + symmetry := mirror(source, current.x, current.y, current.w, current.h) | { + Notice("Cannot mirror tile.") + fail + } + + # In case the window manager opens a window larger than requested ... + + tile(symmetry, pattern, 0, 0, current.w * 2, current.h * 2) + + # Hide it but keep it in case the user wants to save it. + +# WAttrib(symmetry, "canvas=hidden") + + Raise(controls) + + return + +end + +# Terminate program execution. + +procedure quit_cb() + + exit() + +end + +procedure refresh_cb() + + tile(source, pattern, current.x, current.y, current.w, current.h) + + return + +end + +# Callback procedure to allow use of standard tile sizes. + +procedure size_cb(vidget, value) + local dim + + check_source() | fail + + if value[1] == "set @Z" then { + set_size() + return + } + + value[1] ? { + dim := tab(upto('x')) + } + + check(current.w <- current.h <- dim) | fail + + show() + + return + +end + +# Setting of specific selection rectangle values. + +procedure set_size() + + repeat { + if TextDialog("Set values:", + ["x", "y", "w", "h"], + [current.x, current.y, current.w, current.h ] + ) == "Cancel" then fail + check( + current.x <- integer(dialog_value[1]) & + current.y <- integer(dialog_value[2]) & + current.w <- integer(dialog_value[3]) & + current.h <- integer(dialog_value[4]) + ) | { + Notice("Invalid value.") + next + } + show() + return + } + +end + +# Keyboard shortcuts. + +procedure shortcuts(e) + + case type(e) of { + "string" : { + if &meta then case map(e) of { # fold case + "m" : mirror_cb() + "o" : get_image() + "q" : exit() + "s" : save_cb() + "z" : set_size() + } + } + "integer" : { + if &meta then { # nudge dimensions + if check( + case e of { + Key_Left : current.w <- current.w - 1 + Key_Right : current.w <- current.w + 1 + Key_Up : current.h <- current.h - 1 + Key_Down : current.h <- current.h + 1 + } + ) then show() else fail + } + else { # nudge location + if check ( + case e of { + Key_Left : current.x <- current.x - 1 + Key_Right : current.x <- current.x + 1 + Key_Up : current.y <- current.y - 1 + Key_Down : current.y <- current.y + 1 + } + ) then show() else fail + } + } + } + + return + +end + +# Show selection tiled. + +procedure show() + local x, y, w, h + + check_source() | fail + + x := current.x + y := current.y + w := current.w + h := current.h + + if w < 0 then + x -:= (w := -w) + if h < 0 then + y -:= (h := -h) + + tile(source, pattern, x, y, w, h) + + update() + + return + +end + +# Save current selection. + +procedure save_cb() + + check_source() | fail + + return snapshot(source, current.x, current.y, current.w, current.h) + +end + +# Check for source image. + +procedure check_source() + + \source | { + Notice("No source image.") + fail + } + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=360,243", "bg=pale gray", "label=Tile Finder"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,360,243:Tile Finder",], + ["adjust:Menu:pull::131,1,50,21:Adjust",adjust_cb, + ["home","w max","h max","w * 2","h * 2", + "w / 2","h / 2","w = 1","h = 1"]], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["open @O","save @S","save mirrored","quit @Q"]], + ["line1:Line:::0,22,360,22:",], + ["location:Menu:pull::35,0,64,21:Location",location_cb, + ["nw","ne","se","sw","center", + "x max","y max","x * 2","y * 2","x / 2", + "y / 2"]], + ["mirror:Button:regular::100,41,58,20:mirror",mirror_cb], + ["refresh:Button:regular::22,41,58,20:refresh",refresh_cb], + ["size:Menu:pull::98,0,36,21:Size",size_cb, + ["set @Z","4x4","8x8","16x16","32x32", + "64x64","72x72","96x96","100x100","128x128", + "200x200","256x256","400x400","512x512"]], + ["marker:Rect:invisible::8,110,32,20:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/flake.icn b/ipl/gprogs/flake.icn new file mode 100644 index 0000000..21b7d00 --- /dev/null +++ b/ipl/gprogs/flake.icn @@ -0,0 +1,94 @@ +############################################################################ +# +# File: flake.icn +# +# Subject: Program to draw a fractal snowflake +# +# Author: Stephen B. Wampler +# +# Date: August 14, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.0 +# +############################################################################ +# +# +# Comments: This program display a fractal snowflake of specified +# order. Options exist to do colors, etc. +# See the procedure 'helpmsg' for command line options +# +# An order 4 snowflake is particularly nice. +# +# Waits for a window event before closing window +# +############################################################################ +# +# Links: glib, wopen +# +############################################################################ +# +# Requires: Version 9 graphics and co-expressions (for glib.icn) +# +############################################################################ + +link glib +link wopen + +global win, mono, h, w +global Window, XMAX, YMAX +global nextcolor + +procedure main (args) + local nextarg, arg, n, doclip, docolor, Cpoly + + XMAX := YMAX := 700 # physical screen size + w := h := 1.0 + + nextarg := create !args + while arg := @nextarg do { + if arg == ("-help"|"-h") then stop(helpmsg()) + else if arg == "-n" then n := integer(@nextarg) + else if arg == "-clip" then doclip := "yes" + else if arg == "-color" then docolor := "yes" + } + + /n := 3 # default order + + if \doclip then { + Cpoly := [ # a simple convext polygon to clip against + [0.3,0.4],[0.5,0.8],[0.7,0.4] + ] + } + + win := WOpen("label=Fractal Snowflake", "width="||XMAX, "height="||YMAX) + mono := WAttrib (win, "depth") == "1" + Window := set_window(win, point(0,0), point(w,h), + viewport(point(0,0), point(XMAX, YMAX), win)) + + if \docolor then + nextcolor := create vpara([0,0,65535], [65535,0,0], |((0 to 12)/12.0)) + + EraseArea(win) + + Fg(win, "black") + + fract_flake(Window, point(0.20,0.33), point(0.80,0.33), n, 1, Cpoly) + + Event(win) + close(win) +end + +procedure helpmsg() + write("Usage: Flake [-n order] [-clip] [-color]") + write(" where") + write(" -n order -- Depth of recursion {3}") + write(" -clip -- Clip to a convex polygon") + write(" -color -- Color cycle while drawing") + return +end diff --git a/ipl/gprogs/floats.icn b/ipl/gprogs/floats.icn new file mode 100644 index 0000000..9061c28 --- /dev/null +++ b/ipl/gprogs/floats.icn @@ -0,0 +1,77 @@ +############################################################################ +# +# File: floats.icn +# +# Subject: Program to count floats +# +# Author: Ralph E. Griswold +# +# Date: July 15, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program analyzes the floats in a drawdown as a BLP or row file +# from standard input. +# +############################################################################ +# +# Links: pattread, patxform +# +############################################################################ + +link pattread +link patxform + +procedure main() + local front, back, black, white + + front := pattread() + + back := pinvert(front) + + analyze("Front weft floats", front, "0") + + front := protate(front) + + analyze("Front warp floats", front, "1") + analyze("Back weft floats", back, "0") + + back := protate(back) + + analyze("Back warp floats", back, "1") + +end + +procedure analyze(caption, rows, color) + local counts, length, row + + counts := table(0) + + every row := !rows do { + row ? { + while tab(upto(color)) do { + length := *tab(many(color)) + if length > 2 then counts[length] +:= 1 + } + } + } + + if *counts = 0 then return + + write(caption) + + counts := sort(counts, 3) + + write() + + while write("\t", get(counts), "\t", get(counts)) + + write() + + return + +end diff --git a/ipl/gprogs/flohisto.icn b/ipl/gprogs/flohisto.icn new file mode 100644 index 0000000..59772ac --- /dev/null +++ b/ipl/gprogs/flohisto.icn @@ -0,0 +1,171 @@ +############################################################################ +# +# File: flohisto.icn +# +# Subject: Program to display float histograms +# +# Author: Ralph E. Griswold +# +# Date: June 28, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program analyzes the floats in BLPs for drawdowns. +# +# The names of BLPs are given on the command line. The output images +# are named <basename>_float.gif +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: link basename, numbers, options, wopen +# +############################################################################ + +link basename +link wopen +link numbers +link pattread + +$define FloatMax 15 +$define Width 300 +$define Gutter 20 +$define Height 250 +$define Delta 9 +$define Gap 4 +$define Xoff 20 +$define Yoff 30 + +procedure main(args) + local front, back, black, white, name, i, canvas + local warp_front, warp_back, weft_front, weft_back, win, input + + every name := !args do { + input := open(name) | stop("Cannot open ", name) + front := pattread(input) + close(input) + back := copy(front) # 0 = black, 1 = white. + every i := 1 to *back do + back[i] := map(back[i], "10", "01") + weft_front := analyze(front, "1") + front := rot(front) + warp_front := analyze(front, "0") + weft_back := analyze(back, "1") + back := rot(back) + warp_back := analyze(back, "0") + win := WOpen("size=" || (2 * Width + 2 * Gutter) || "," || + (2 * Height + 2 * Gutter), "canvas=hidden") | + stop("*** cannot open main window") + CopyArea(plot(warp_front, "warp front"), win, , , , , 0, 0) + CopyArea(plot(weft_front, "weft front"), win, , , , , Width + Gutter, 0) + CopyArea(plot(warp_back, "warp back"), win, , , , , 0, Height + Gutter) + CopyArea(plot(weft_back, "weft back"), win, , , , , Width + Gutter, + Height + Gutter) + WriteImage(win, basename(name, ".blp") || "_floats.gif") + WClose(win) + } + +end + +procedure analyze(rows, color) + local counts, length, row, k, count_list + + counts := table(0) + + every row := !rows do { + row ? { + while tab(upto(color)) do { + length := *tab(many(color)) + if length > 1 then counts[length] +:= 1 + } + } + } + + if *counts = 0 then fail # no floats + + count_list := list(FloatMax, 0) # list of counts + + every k := key(counts) do + if k > FloatMax then count_list[FloatMax] +:= counts[k] + else count_list[k - 1] := counts[k] + + return count_list + +end + +procedure plot(data, legend) + local i, j, scale, maximum, y, width, win + + win := WOpen("size=" || Width || "," || Height, "font=times,10", "canvas=hidden") | + stop("*** cannot open plotting window") + + WAttrib(win, "dx=" || Xoff) + WAttrib(win, "dy=" || (Yoff + Gap)) + + DrawLine(win, 0, 0 - Gap, Width, 0 - Gap) + DrawLine(win, 0, 0 - Gap, 0, Height - Gap) + + DrawString(win, -2, -(18 + Gap), legend) + + if /data then return win + + maximum := max ! data + maximum := integer((maximum + 99.0) / 100) * 100 # get to next hundred + + width := real(Width - 2 * Xoff) + scale := width / maximum + + every i := 0 to 4 do + CenterString(win, (width / 4) * i, 18 - Yoff, (maximum / 4) * i) + + every j := 2 to FloatMax + 1 do { + y := (j - 2) * (Delta + Gap) + FillRectangle(win, 0, y, data[j - 1] * scale, Delta) + if j > FloatMax then j := ">" + RightString(win, 15 - Xoff, y + Gap, j) + } + + return win + +end + +procedure win2rows(win) + local width, height, row, rows, pixel, y + + width := WAttrib(win, "width") + height := WAttrib(win, "height") + + rows := [] + + every y := 0 to height - 1 do { + row := "" + every pixel := Pixel(win, 0, y, width, 1) do + row ||:= if pixel == "0,0,0" then "0" else "1" + put(rows, row) + } + + return rows + +end + +procedure rot(rows) + local cols, row, grid, i + + cols := list(*rows[1], "") + + every row := !rows do { + i := 0 + every grid := !row do + cols[i +:= 1] := grid || cols[i] + } + + return cols + +end diff --git a/ipl/gprogs/fmap2pdb.icn b/ipl/gprogs/fmap2pdb.icn new file mode 100644 index 0000000..91709b9 --- /dev/null +++ b/ipl/gprogs/fmap2pdb.icn @@ -0,0 +1,63 @@ +############################################################################ +# +# File: fmap2pdb.icn +# +# Subject: Program to create custom palettes from color maps +# +# Author: Ralph E. Griswold +# +# Date: May 15, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program builds a palette database from Fracting color maps. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, palettes, xcode +# +############################################################################ + +link basename +link palettes +link xcode + +global PDB_ + +procedure main(args) + local file, input, clist, color, line, name + + every file := !args do { + input := open(file) | { + write(&errout, "*** cannot open ", image(file)) + next + } + name := basename(file, ".map") + clist := [] + while line := read(input) do { + line ? { + tab(upto(&digits)) + color := (tab(many(&digits)) * 257) || "," + tab(upto(&digits)) + color ||:= (tab(many(&digits)) * 257) || "," + tab(upto(&digits)) + color ||:= (tab(many(&digits)) * 257) + } + put(clist, color) + } + close(input) + makepalette(name, clist) | + write(&errout, "*** could not make palette from ", image(file)) + } + + xencode(PDB_, &output) + +end diff --git a/ipl/gprogs/fontpick.icn b/ipl/gprogs/fontpick.icn new file mode 100644 index 0000000..5b5497e --- /dev/null +++ b/ipl/gprogs/fontpick.icn @@ -0,0 +1,163 @@ +############################################################################ +# +# File: fontpick.icn +# +# Subject: Program to show the characters of a font +# +# Author: Gregg M. Townsend +# +# Date: August 23, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: fontpick [fontname] +# +# fontpick is an interactive tool for displaying fonts. Initially, the +# specified font, or the VIB default font, is displayed. To display a +# different font, type its name and press return. To exit, enter Meta-Q +# or click the QUIT button. +# +# Caveats: +# -- any character that is too large is clipped to fit its cell +# -- the window cannot be resized to handle large fonts +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: vsetup +# +############################################################################ + +link vsetup + +global vidgets + + +# main procedure + +procedure main(args) + Window ! put(ui_atts(), args) + vidgets := ui() + setfont(, args[1] | Font()) # display named or default font + repeat ProcessEvent(vidgets["root"], other) +end + + +# setfont(vidget, value) -- display the font named "value" + +procedure setfont(vidget, value) + local ttl, sub, rgn, fontname, x, y, w, h, win + + # ignore return if no name has been entered + if *value = 0 then + return + + # get vidget handles + ttl := vidgets["title"] + sub := vidgets["subtitle"] + rgn := vidgets["region"] + + # display font name in title region + EraseArea(ttl.ux, ttl.uy, ttl.uw, ttl.uh) + EraseArea(sub.ux, sub.uy, sub.uw, sub.uh) + CenterString(ttl.ux + ttl.uw / 2, ttl.uy + ttl.uh / 2, value) + + # open and display the font + EraseArea(rgn.ux, rgn.uy, rgn.uw, rgn.uh) + if win := Clone("font=" || value) then { + dumpfont(win, rgn.ux, rgn.uy, rgn.uw, rgn.uh) + fontname := Font(win) + if fontname ~== value then + CenterString(sub.ux + sub.uw / 2, sub.uy + sub.uh / 2, fontname) + } + else { + CenterString(sub.ux + sub.uw / 2, sub.uy + sub.uh / 2, + "(cannot find font)") + } + + # clear the text entry field to accept the next name + VSetState(vidgets["fontname"], "") + return +end + + +# dumpfont(win, x, y, w, h) -- display the characters of a font + +procedure dumpfont(win, x, y, w, h) + local dx, dy, x1, x2, y1, y2, i, j + + # calculate size of cells + dx := (w - 1.001) / 16.0 + dy := (h - 1.001) / 16.0 + + # draw light gray lines to delimit character cells + Fg("light gray") + every x1 := x + integer(dx * (1 to 15)) do + DrawLine(x1, y, x1, y + h - 1) + every y1 := y + integer(dy * (1 to 15)) do + DrawLine(x, y1, x + w - 1, y1) + Fg("black") + + # display characters, one per cell + every i := 0 to 15 do { + y1 := integer(y + i * dy) + y2 := integer(y + (i + 1) * dy) + every j := 0 to 15 do { + x1 := integer(x + j * dx) + x2 := integer(x + (j + 1) * dx) + Clip(win, x1 + 1, y1 + 1, x2 - x1 - 1, y2 - y1 - 1) + CenterString(win, (x1 + x2) / 2, (y1 + y2) / 2, char(16 * i + j)) + } + } + return +end + + +# revent(v, e, x, y) -- pass region event to font name vidget + +procedure revent(v, e, x, y) + return VEvent(vidgets["fontname"], e, x, y) +end + + +# other(e) -- pass event outside of regions to font name vidget +# +# Also handles meta-Q event rejected by other vidgets. + +procedure other(e) + if &meta & map(e) == "q" then + exit() + return VEvent(vidgets["fontname"], e, &x, &y) +end + + +# quit() -- process QUIT button + +procedure quit() + exit() +end + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=512,640", "bg=pale gray"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,512,640:",], + ["fontname:Text::51:5,616,444,19:font name: \\=",setfont], + ["quit:Button:regular::471,615,35,20:QUIT",quit], + ["subtitle:Rect:invisible::7,31,496,25:",revent], + ["title:Rect:invisible::7,6,496,25:",revent], + ["region:Rect:sunken::8,56,492,553:",revent], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/fractclr.icn b/ipl/gprogs/fractclr.icn new file mode 100644 index 0000000..fa2686e --- /dev/null +++ b/ipl/gprogs/fractclr.icn @@ -0,0 +1,36 @@ +############################################################################ +# +# File: fractclr.icn +# +# Subject: Program to map Fractint color maps to Icon color lists +# +# Author: Ralph E. Griswold +# +# Date: January 1, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts Fractint .map files to lists of Icon colors. +# +############################################################################ + +procedure main() + local line, output + + while line := read() do + line ? { + tab(upto(&digits)) + writes(tab(many(&digits)) * 256, ",") + tab(upto(&digits)) + writes(tab(many(&digits)) * 256, ",") + tab(upto(&digits)) + writes(tab(many(&digits)) * 256) + if not pos(0) then write(output, "\t", tab(0)) + else write(output) + } + +end diff --git a/ipl/gprogs/fractlin.icn b/ipl/gprogs/fractlin.icn new file mode 100644 index 0000000..c9a3639 --- /dev/null +++ b/ipl/gprogs/fractlin.icn @@ -0,0 +1,78 @@ +############################################################################ +# +# File: fractlin.icn +# +# Subject: Program to demonstrate fractal lines +# +# Author: Stephen Wampler +# +# Date: August 3, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.0 +# +############################################################################ +# +# +# Comments: This program shows how fractal lines work. +# +# See the procedure 'helpmsg' for command line options +# +# Waits for a window event before closing window +# +############################################################################ +# +# Links: glib, wopen +# +############################################################################ +# +# Requires: Version 9 graphics and co-expressions (for glib.icn) +# +############################################################################ + +link glib +link wopen + +global win, mono, h, w +global Window, XMAX, YMAX + +procedure main (args) + local nextarg, arg, i + + XMAX := YMAX := 700 # physical screen size + w := h := 1.0 + + nextarg := create !args + while arg := @nextarg do { + if arg == ("-help"|"-h") then stop(helpmsg()) + } + + win := WOpen("label=Fractal Lines", "width="||XMAX, "height="||YMAX) + mono := WAttrib (win, "depth") == "1" + Window := set_window(win, point(0,0), point(w,h), + viewport(point(0,0), point(XMAX, YMAX), win)) + + EraseArea(win) + + Fg(win, "black") + + every i := 1 to 10 do { + fract_line(Window, point(0.25,0.25), point(0.50,0.67), i/10.0) + fract_line(Window, point(0.50,0.67), point(0.75,0.25), i/10.0) + fract_line(Window, point(0.75,0.25), point(0.25,0.25), i/10.0) + } + + Event(win) + close(win) +end + +procedure helpmsg() + write("Usage: Fract") + return +end + diff --git a/ipl/gprogs/fstarlab.icn b/ipl/gprogs/fstarlab.icn new file mode 100644 index 0000000..e9a767f --- /dev/null +++ b/ipl/gprogs/fstarlab.icn @@ -0,0 +1,70 @@ +############################################################################ +# +# File: fstarlab.icn +# +# Subject: Program to draw fractal stars +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws fractal "stars". For a discussion of fractal +# stars, see +# +# Fractals; Endlessly Repeated Geometrical Figures, Hans Lauwerier, +# Princeton University Press, 1991, pp. 72-77. +# +# and +# +# Geometric and Artistic Graphics; Design Generation with +# Microcomputers, Jean-Paul Delahaye, Macmillan, 1987, pp. 55-63. +# +# The window is square. The window size can be given on the command line, +# default 600. +# +# The present user interface is crude. To see all the fractal stars +# that are provided by default, type +# +# all +# +# from standard input. After each star is drawn, the program waits +# for an event before going on to the next star. +# +# Alternatively, a single star can be drawn by typing its name preceded +# by an equals sign. The names are fstar01 through fstar13. For example, +# +# =fstar09 +# +# draws the ninth star. +# +# In future extensions, provision will be made for user-defined stars. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: drawlab, fstars, fstartbl +# +############################################################################ + +link drawlab +link fstars +link fstartbl + +global size + +procedure main(argl) + + size := integer(argl[1]) | 600 + + drawlab(fstar, fstartbl, "fractal stars") + +end diff --git a/ipl/gprogs/gallery.icn b/ipl/gprogs/gallery.icn new file mode 100644 index 0000000..4dcd0a7 --- /dev/null +++ b/ipl/gprogs/gallery.icn @@ -0,0 +1,545 @@ +############################################################################ +# +# File: gallery.icn +# +# Subject: Program to display many images at once +# +# Author: Gregg M. Townsend +# +# Date: August 3, 2005 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: gallery [-{whs}nnn] [-{rmtud}] file... +# +# Gallery displays multiple images in a single window. The images +# are shrunken by resampling and tiled in columns or rows. +# +# GIF and XPM format images are always supported. JPEG format is +# supported when built by Jcon. JPEG, PPM, TIFF, PNG, and RLE formats +# are also available under Unix if the necessary conversion utilities +# are available in the shell search path. +# +# When the window fills, diagonal lines in the extreme corners of the +# window indicate that you can press Enter for the next screenful. +# Solid triangles appear when there are no more images; press Q to exit. +# +# At either of those pauses, pressing 'S' brings up a dialog for saving +# a snapshot of the window. Clicking the left mouse button on an +# image displays a popup window with information about the image. A +# second click dismisses the popup, as does the space bar or Enter key. +# The right mouse button activates the same popup momentarily until +# the button is released. +# +# -wnnn sets the maximum width for displaying an image; +# -hnnn sets the maximum height. -snnn sets both. +# By default, sizes are chosen automatically, subject to a minimum +# size of 32x32, to allow all images to fit in a single window. +# +# -r arranges images in rows instead of columns. +# -m maximizes the window size before displaying images. +# -t trims file names of leading path components and extensions. +# -u shows images completely unlabeled. +# -d prints some debugging information. +# +# The standard Window() options are accepted and can be used to +# set the window size and other parameters. A default gamma value +# of 1.0 can be changed by using (e.g.) "-A gamma=1.6". +# +# -cn and -gn options, which formerly selected a color palette, +# are now ignored. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, imscolor, interact, options, io, random, cfunc +# +############################################################################ + + +# TO DO: +# +# improve prompts -- something more obvious & intuitive + + +link graphics +link imscolor +link interact +link options +link io +link random + + +$define Gap 4 # gap between images +$define MinWidth 32 # minimum width if auto-scaled +$define MinHeight 32 # minimum height if auto-scaled + + +record imrec(win, fullw, fullh) +record area(fname, x, y, w, h, iw, ih) + +global opts # command options +global tempname # temporary file name + +global ww, wh, fh, fw # window dimensions +global maxw, maxh # maximum size of displayed image + +global areas # areas used for display + + + +procedure main(args) + local cw, ch, bigh, bigw, x, y, w, h, gg, aspr, aspmax, horz + local fname, label, f, tw, s, nchars, nlines, img, imwin, e + + # generate a random name for the temporary file + randomize() + tempname := "/tmp/gal" || right(?99999, 5, "0") || ".tmp" + + # open the window and process options + Window("size=800,500", "bg=pale gray", "font=sans,8", "gamma=1.0", args) + opts := options(args, "g+c+w+h+s+rmtud") + if \opts["m"] then + WAttrib("canvas=maximal") + if *args = 0 then + stop("usage: ", &progname, " [-{gc}n] [-{whd}nnn] [-{mtv}] file...") + + # allow user resizing of window + &error := 1 + WAttrib("resize=on") + &error := 0 + + # record window dimensions + ww := WAttrib("width") + wh := WAttrib("height") + if \opts["u"] then + fh := 0 + else + fh := WAttrib("fheight") + fw := WAttrib("fwidth") + maxw := \opts["w"] | \opts["s"] | 2 * \opts["h"] + maxh := \opts["h"] | \opts["s"] | 2 * \opts["w"] + + # If no image size specified, try to guess to fill the window + if /maxw then + layout(*args) + + aspmax := real(maxw) / real(maxh) + + # Display the files. + x := y := Gap + bigw := bigh := 0 + areas := list() + every fname := !args do { + + close(\f) + close(\imwin) + f := imwin := &null + + # Check for an interrupt + while *Pending() > 0 do + if Event() === QuitEvents() then + return + + # Get the next file and translate its image. + f := open(fname) | + { write(&errout, fname, ": can't open"); next } + + # Read the image, full sized, into a scratch canvas + if not (img := rdimage(fname, f, maxw, maxh)) then + { write(&errout, fname, ": can't decode"); next } + imwin := img.win + + # Scale the image to the desired size + w := WAttrib(imwin, "width") + h := WAttrib(imwin, "height") + aspr := real(w) / real(h) + if w > maxw | h > maxh then { + if aspr > aspmax then { + w := maxw + h := maxw / aspr + } + else { + w := maxh * aspr + h := maxh + } + w <:= 1 + h <:= 1 + Zoom(imwin, , , , , , , w, h) + } + + # Trim the file name if so requested. + if \opts["t"] then + fname ? { + while tab(upto('/') + 1) + ="cache" + label := tab(upto('.') | 0) + } + else + label := fname + + # Calculate the area needed for display + cw := w # cell width + if /opts["u"] then + cw <:= TextWidth(label) # ensure room for label + ch := h + fh # cell height + + # Place the new image on a new row or new window if needed. + if x + cw > ww | y + ch > wh then { # if row or column is full + + if /opts["r"] then { + x +:= bigw + Gap # start new column + y := Gap + bigw := 0 + } + else { + x := Gap # start new row + y +:= bigh + Gap + bigh := 0 + } + + if x + cw > ww | y + ch > wh then { + # no room for new row or column + pause() # wait for OK + EraseArea() # clear the window + ww := WAttrib("width") + wh := WAttrib("height") + x := y := Gap + bigw := bigh := 0 + areas := list() + } + } + + # Draw the image and its label. + CopyArea(imwin, &window, 0, 0, w, h, x, y) + if /opts["u"] then + DrawString(x, y + h + fh - WAttrib("descent"), label) + + # Record the space it occupies + put(areas, area(fname, x - Gap / 2, y - Gap / 2, w + Gap, h + fh + Gap, + img.fullw, img.fullh)) + + # Move on to next position. + if /opts["r"] then + y +:= ch + Gap + else + x +:= cw + Gap + bigh <:= ch + bigw <:= cw + } + + # All images have been displayed. Wait for "q" before exiting. + close(\f) + close(\imwin) + + w := WAttrib("width") + h := WAttrib("height") + gg := 2 * Gap - 1 + FillPolygon(0, 0, 0, gg - 1, gg - 1, 0) + FillPolygon(0, h, 0, h - gg, gg - 1, h - 1) + FillPolygon(w, 0, w - gg, 0, w - 1, gg - 1) + FillPolygon(w, h, w - gg, h - 1, w - 1, h - gg) + + while e := Event() do case e of { # wait for event + QuitEvents(): exit() # quit on "q" etc + !"sS": snapshot() # save window shapshot + &lpress | &rpress: info(e) # display info about image + } +end + + + +# layout(n) -- calculate layout for n images + +$define GuessAspect 1.5 # aspect ratio guess used for layout + +procedure layout(n) + local aspf, nhigh, nwide + + aspf := real(ww) / real(wh) / GuessAspect + nhigh := integer(sqrt(n / aspf) + 0.5) + nhigh <:= 1 + nwide := (n + nhigh - 1) / nhigh + maxw := ((ww - Gap) / nwide) - Gap + maxh := ((wh - Gap) / nhigh) - Gap - fh + maxw <:= MinWidth + maxh <:= MinHeight + + if \opts["d"] then + write(&errout, "npix=", n, " aspf=", aspf, " nhigh=", nhigh, + " nwide=", nwide, " maxh=", maxh, " maxw=", maxw) + return +end + + + +## pause() -- wait for clearance to start a new window + +procedure pause() + local w, h, gg, e + + while *Pending() > 0 do # consume and ignore older events + Event() + + w := WAttrib("width") + h := WAttrib("height") + gg := 2 * Gap - 1 + DrawLine(0, gg - 1, gg - 1, 0) # draw diagonals to indicate pause + DrawLine(0, h - gg, gg - 1, h - 1) + DrawLine(w - gg, 0, w - 1, gg - 1) + DrawLine(w - gg, h - 1, w - 1, h - gg) + + while e := Event() do case e of { # wait for event + QuitEvents(): exit() # quit on "q" etc + !" \t\r\n": break # continue on "\r" etc + !"sS": snapshot() # save window shapshot + &lpress | &rpress: info(e) # display info about image + } + return +end + + + +## info(event) -- display info about image under the mouse + +$define InfoMargin 10 # margin around image +$define InfoHeight 80 # text area height +$define InfoWidth 300 # text area width + +procedure info(e) + local a, w, h, wmin, wmax, hmax + + wmin := InfoWidth + 2 * InfoMargin + wmax := WAttrib("width") - 4 * InfoMargin + hmax := WAttrib("height") - 5 * InfoMargin - InfoHeight + + every a := !areas do + if InBounds(a.x, a.y, a.w, a.h) then { + w := a.iw + h := a.ih + if w >:= wmax then + h := a.ih * w / a.iw + if h >:= hmax then + w := a.iw * h / a.ih + wmin <:= w + 2 * InfoMargin + Popup(, , wmin, h + InfoHeight + 3 * InfoMargin, popinfo, a, e, w, h) + break + } + return +end + + + +## popinfo(area, event, w, h) -- display info in the popup +# +# if event was &rpress, wait for &rrelease +# otherwise wait for &lpress, Enter, or space to dismiss + +procedure popinfo(a, e, w, h) + local f, i, n, x, y + + f := open(a.fname) + seek(f, 0) + n := where(f) + seek(f, 1) + i := rdimage(a.fname, f, w, h) | fail + + x := (WAttrib("clipw") - w) / 2 + y := InfoMargin + Zoom(i.win, &window, , , , , x, y, w, h) + + Font("sans,bold,12") + WAttrib("leading=16") + GotoXY(0, InfoMargin + h + InfoMargin + WAttrib("ascent")) + WWrite(" ", a.fname) + WWrite(" ", a.iw, " x ", a.ih, " pixels") + WWrite(" ", n, " bytes") + WWrite(" ", iformat(f), " format") + + if e === &rpress then + until Event() === &rrelease # dismiss upon button release + else { + until Event() === &lrelease # consume matching release + until Event() === &lrelease | !" \n\r" # wait for dismissal + } + + WClose(i.win) + return +end + + + +## iformat(f) -- return image format of file f + +procedure iformat(f) + local s + + seek(f, 1) + s := reads(f, 1024) | fail + seek(f, 1) + s ? { + if ="GIF8" then return "GIF" + if ="\x89PNG" then return "PNG" + if ="\xFF\xD8\xFF" then return "JPEG" + if ="MM\x00\x2A" then return "TIFF" + if ="II\x2A\x00" then return "TIFF" + if =("P1" | "P4") then return "PBM" + if =("P2" | "P5") then return "PGM" + if =("P3" | "P6") then return "PPM" + if ="\x52\xCC" then return "RLE" + if ="BM" then return "BMP" + if find("XPM") then return "XPM" + fail + } +end + + + +## rdimage(fname, f, maxw, maxh) -- read image into scratch window + +procedure rdimage(fname, f, maxw, maxh) + local iwin + + case iformat(f) of { + "GIF" | "XPM": iwin := load(fname) + "PNG": iwin := convert(fname, "pngtopnm") + "TIFF": iwin := convert(fname, "tifftopnm") + "PBM" | "PGM" | "PPM": iwin := convert(fname, "cat") + "RLE": iwin := convert(fname, "rletopnm") + "BMP": iwin := convert(fname, "bmptoppm") + "JPEG": return jpegread(fname, maxw, maxh) + } + + return imrec(\iwin, WAttrib(iwin, "width"), WAttrib(iwin, "height")) +end + + + +## convert(fname, utilname) -- read image by converting through PPM to GIF + +procedure convert(fname, utilname) + needprog(utilname) | fail + needprog("ppmquant") | fail + needprog("ppmtogif") | fail + return mkgif(utilname || + " 2>/dev/null | ppmquant 256 2>/dev/null | ppmtogif 2>/dev/null", + fname) +end + + + +## mkgif(cmd, fname) -- run filter to produce GIF file + +procedure mkgif(cmd, fname) + local win, f + + remove(tempname) + cmd := "<\"" || fname || "\" " || cmd || " >" || tempname + if \opts["d"] then + write(&errout, "+ ", cmd) + system(cmd) + f := open(tempname) | fail + win := load(tempname) + close(f) + remove(tempname) + return \win +end + + + +## jpegread(fname, maxw, maxh) -- read JPEG image + +procedure jpegread(fname, maxw, maxh) + local scale, iwin, irec + + $ifdef _JAVA + iwin := load(fname) + return imrec(\iwin, WAttrib(iwin, "width"), WAttrib(iwin, "height")) + $else + needprog("djpeg") | fail + irec := imrec() + if jsize(irec, fname) then + scale := jscale(irec, \maxw, \maxh) | "" + else + scale := "" + irec.win := mkgif("djpeg " || scale || " -g 2>/dev/null", fname) | fail + /irec.fullw := WAttrib(iwin, "width") + /irec.fullh := WAttrib(iwin, "height") + return irec + $endif + + +end + + + +## jsize(irec, fname) -- set fullw and fullh fields for JPEG image + +procedure jsize(irec, fname) + local s, p, line, w, h + + s := "" + p := open("rdjpgcom -verbose " || fname, "p") | fail + while line := read(p) do line ? { + ="JPEG image is " | next + w := tab(many(&digits)) | next + ="w * " | next + h := tab(many(&digits)) | next + ="h, " | next + close(p) + irec.fullw := integer(w) + irec.fullh := integer(h) + return + } + close(p) + fail +end + + + +## jscale(irec, maxw, maxh) -- determine scaling for faster JPEG reading + +procedure jscale(irec, maxw, maxh) + local m + + m := irec.fullw / maxw + m <:= irec.fullh / maxh + if m >= 8 then return "-scale 1/8" + if m >= 4 then return "-scale 1/4" + if m >= 2 then return "-scale 1/2" + return "" +end + + + + + +## load(fname) -- read image using WOpen + +procedure load(fname) + return WOpen("canvas=hidden", "bg=" || WAttrib("bg"), + "gamma=" || WAttrib("gamma"), "image=" || fname) +end + + + +## needprog(s) -- check for presence of program s in $PATH +# +# Fails if the program is not available. +# Issues a diagnostic only once per program. + +procedure needprog(s) + static ptable + initial ptable := table() + + /ptable[s] := pathfind(s, map(getenv("PATH"), ":", " ")) | + (write(&errout, "can't find program \"", s, "\" in $PATH") & "") + return "" ~=== ptable[s] +end diff --git a/ipl/gprogs/gamma.icn b/ipl/gprogs/gamma.icn new file mode 100644 index 0000000..0d9a57d --- /dev/null +++ b/ipl/gprogs/gamma.icn @@ -0,0 +1,220 @@ +############################################################################ +# +# File: gamma.icn +# +# Subject: Program to perform gamma correction on images +# +# Author: Ralph E. Griswold +# +# Date: March 5, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program allows changing the gamma correction for images. It can +# be used, for example, to desaturate images for use as backgrounds. +# Note: Fully saturated nd fully unsaturated colors are not affected by +# gamma correction. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, vfilter, vsetup +# +############################################################################ + +link interact +link vfilter +link vsetup + +global continuous_vidget # continuous update toggle +global gamma # current gamma value +global gamma_vidget # gamma vidget +global default_gamma # original gamma value +global name # name of current image file +global pane # window for current image +global vidgets # table of vidgets + + + +procedure main() + + vidgets := ui() + + continuous_vidget := vidgets["continuous"] + gamma_vidget := vidgets["gamma"] + + VSetState(continuous_vidget, "1") + + default_gamma := WAttrib("gamma") + + set_gamma(default_gamma) + + GetEvents(vidgets["root"], , shortcuts) + +end + +procedure continuous_cb(vidget, value) + + if \value then VSetFilter(gamma_vidget, &null) else + VSetFilter(gamma_vidget, "1") + + return + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "load @L" : load_image() + "quit @Q" : exit() + "save @S" : save_image() + } + + return + +end + +procedure gamma_cb(vidget, value) + + set_gamma(10.0 ^ value) + + return + +end + +procedure load_image() + + WClose(\pane) + + repeat { + if OpenDialog("Load image file:") == "Cancel" then fail + pane := WOpen("label=" || dialog_value, "image=" || dialog_value, + "gamma=" || gamma) | { + Notice("Cannot open image file.") + next + } + name := dialog_value + Raise() + return + } + +end + +procedure reset_cb() + + set_gamma(default_gamma) + +end + +procedure save_image() + + WAttrib(\pane, "gamma=" || default_gamma) | { + Notice("No image loaded.") + fail + } + snapshot(pane) + WAttrib(pane, "gamma=" || gamma) + + return + +end + +procedure set_cb() + + repeat { + if OpenDialog("Set gamma value:", gamma, 10) == "Cancel" then fail + if 0.0 <= numeric(dialog_value) <= 100.0 then { + set_gamma(dialog_value) + return + } + else { + Notice("Invalid gamma value.") + next + } + } + +end + +procedure set_gamma(value) + + gamma := value + + WAttrib(\pane, "gamma=" || gamma) + VSetState(gamma_vidget, log(value, 10)) + show_gamma() + ReadImage(\pane, name) + Raise() + + return + +end + +procedure shortcuts(value) + + if &meta then case map(value) of { + "l" : load_image() + "q" : exit() + "r" : set_gamma(default_gamma) + "s" : save_image() + } + + return + +end + +procedure show_gamma() + static old_gamma, x, y + + initial { + old_gamma := "" + x := vidgets["show_gamma"].ax + y := vidgets["show_gamma"].ay + } + + WAttrib("drawop=reverse") + DrawString(x, y, old_gamma) + DrawString(x, y, gamma) + WAttrib("drawop=copy") + + old_gamma := gamma + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=337,210", "bg=pale gray"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,337,210:",], + ["10:Label:::109,97,21,13:1.0",], + ["20:Label:::193,97,28,13:10.0",], + ["3:Label:::23,97,21,13:0.1",], + ["continuous:Button:regular:1:12,120,126,20:continuous update",continuous_cb], + ["file:Menu:pull::0,2,36,21:File",file_cb, + ["load @L","save @S","quit @Q"]], + ["gamma:Scrollbar:h::12,62,305,16:-1.0,2.0,2.0",gamma_cb], + ["glabel:Label:::102,37,112,13:gamma correction",], + ["label1:Label:::276,97,35,13:100.0",], + ["label2:Label:::117,162,56,13:gamma = ",], + ["line1:Line:::0,23,336,23:",], + ["line2:Line:::34,80,34,90:",], + ["line3:Line:::209,80,209,90:",], + ["line4:Line:::121,80,121,90:",], + ["line5:Line:::295,80,295,90:",], + ["reset:Button:regular::57,159,42,20:reset",reset_cb], + ["set:Button:regular::12,159,35,20:set",set_cb], + ["show_gamma:Button:regularno::179,174,35,20:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/gif2blp.icn b/ipl/gprogs/gif2blp.icn new file mode 100644 index 0000000..ff603ed --- /dev/null +++ b/ipl/gprogs/gif2blp.icn @@ -0,0 +1,53 @@ +############################################################################ +# +# File: gif2blp.icn +# +# Subject: Program to convert B&W GIF to a BLP +# +# Author: Ralph E. Griswold +# +# Date: March 4, 2003 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# AD HOC. Assumes any non-black pixel is white. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: patxform, wopen +# +############################################################################ + +link patxform +link wopen + +procedure main(args) + local width, height, row, p, y, rows + + WOpen("image=" || args[1], "canvas=hidden") | + stop("*** cannot open image") + + width := WAttrib("width") + height := WAttrib("height") + + rows := [] + + every y := 0 to height - 1 do { + row := "" + every p := Pixel(0, y, width, 1) do + if ColorValue(p) == "0,0,0" then row ||:= "1" + else row ||:= "0" + put(rows, row) + } + + write(rows2pat(rows)) + +end diff --git a/ipl/gprogs/gif2isd.icn b/ipl/gprogs/gif2isd.icn new file mode 100644 index 0000000..3eeac2b --- /dev/null +++ b/ipl/gprogs/gif2isd.icn @@ -0,0 +1,131 @@ +############################################################################ +# +# File: gif2isd.icn +# +# Subject: Program to produce a ISD from bi-level image +# +# Author: Ralph E. Griswold +# +# Date: November 17, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes a B&W GIF image whose name is given on the +# command line and writes an ISD for a draft to standard output. +# +# If the GIF is not strictly B&W, non-black pixels are assumed to +# be white. +# +############################################################################ +# +# Links: graphics, weavutil, xcode +# +############################################################################ + +link graphics +link weavutil +link xcode + +procedure main(args) + local rows, cols, treadling, threading, count, tieup, y, width, height + local shafts, treadles, i, tie_line, row, treadle, draft, p + + WOpen("image=" || args[1], "canvas=hidden") | + stop("*** cannot open image") + + width := WAttrib("width") + height := WAttrib("height") + + rows := [] # start with empty list + + every y := 0 to height - 1 do { + row := "" + every p := Pixel(0, y, width, 1) do + if ColorValue(p) == "0,0,0" then row ||:= "1" + else row ||:= "0" + put(rows, row) + } + + cols := rot(rows) # rotate to get columns + + treadles := examine(rows) # get treadles + shafts := examine(cols) # get shafts + + treadling := [] # construct treadling sequence + every put(treadling, treadles[!rows]) + + threading := [] # construct threading sequence + every put(threading, shafts[!cols]) + + tieup := [] + + every row := key(treadles) do { # get unique rows + treadle := treadles[row] # assigned treadle number + tie_line := repl("0", *shafts) # blank tie-up line + every i := 1 to *row do # go through row + if row[i] == "1" then # if warp on top + tie_line[threading[i]] := "1" # mark shaft position + put(tieup, tie_line) # add line to tie-up + } + + draft := isd("gif2isd") + + draft.threading := threading + draft.treadling := treadling + draft.shafts := *shafts + draft.treadles := *treadles + draft.width := *shafts + draft.height := *treadles + draft.tieup := tieup + draft.color_list := ["black", "white"] + draft.warp_colors := list(*threading, 1) + draft.weft_colors := list(*treadling, 2) + + write(xencode(draft)) + +end + +procedure tromp(treadle) + local result + + result := "" + + treadle ? { + every result ||:= upto("1") || "," + } + + return result[1:-1] + +end + +procedure examine(array) + local count, lines, line + + lines := table() # table to be keyed by line patterns + count := 0 + + every line := !array do # process lines + /lines[line] := (count +:= 1) # if new line, insert with new number + + return lines + +end + +procedure rot(rows) + local cols, row, grid, i + + cols := list(*rows[1], "") + + every row := !rows do { + i := 0 + every grid := !row do + cols[i +:= 1] := grid || cols[i] + } + + return cols + +end diff --git a/ipl/gprogs/gif2rows.icn b/ipl/gprogs/gif2rows.icn new file mode 100644 index 0000000..ff93154 --- /dev/null +++ b/ipl/gprogs/gif2rows.icn @@ -0,0 +1,48 @@ +############################################################################ +# +# File: gif2rows.icn +# +# Subject: Program to convert B&W GIF to 0/1 rows +# +# Author: Ralph E. Griswold +# +# Date: August 11, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# AD HOC. Assumes any non-black pixel is white. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(args) + local width, height, row, p, y + + WOpen("image=" || args[1], "canvas=hidden") | + stop("*** cannot open image") + + width := WAttrib("width") + height := WAttrib("height") + + every y := 0 to height - 1 do { + row := "" + every p := Pixel(0, y, width, 1) do + if ColorValue(p) == "0,0,0" then row ||:= "1" + else row ||:= "0" + write(row) + } + +end diff --git a/ipl/gprogs/gif2wif.icn b/ipl/gprogs/gif2wif.icn new file mode 100644 index 0000000..37678b1 --- /dev/null +++ b/ipl/gprogs/gif2wif.icn @@ -0,0 +1,196 @@ +############################################################################ +# +# File: gif2wif.icn +# +# Subject: Program to produce a WIF from black & white image +# +# Author: Ralph E. Griswold +# +# Date: May 7, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes the name of a GIF file for a black & white image +# and outputs a WIF for a corresponding draft. If the GIF is not +# strictly black & white, all non-black pixels are interpreted as +# white. +# +############################################################################ +# +# Links: graphics +# +############################################################################ +# +# Requires: Version 9 graphics + +############################################################################ + +link graphics + +procedure main(args) + local rows, cols, treadling, threading, count, tieup, y, width, height + local shafts, treadles, i, tie_line, row, treadle, draft, p + + WOpen("image=" || args[1], "canvas=hidden") | + stop("*** cannot open image") + + width := WAttrib("width") + height := WAttrib("height") + + rows := [] # start with empty list + + every y := 0 to height - 1 do { + row := "" + every p := Pixel(0, y, width, 1) do + if ColorValue(p) == "0,0,0" then row ||:= "1" + else row ||:= "0" + put(rows, row) + } + + cols := rot(rows) # rotate to get columns + + treadles := examine(rows) # get treadles + shafts := examine(cols) # get shafts + + treadling := [] # construct treadling sequence + every put(treadling, treadles[!rows]) + + threading := [] # construct threading sequence + every put(threading, shafts[!cols]) + + tieup := table() + + every row := key(treadles) do { # get unique rows + treadle := treadles[row] # assigned treadle number + tie_line := repl("0", *shafts) # blank tie-up line + every i := 1 to *row do # go through row + if row[i] == "1" then # if warp on top + tie_line[threading[i]] := "1" # mark shaft position + tieup[treadle] := tie_line # add line to tie-up + } + + # Now output the WIF. + + write("[WIF]") + write("Version=1.1") + write("Date=" || &dateline) + write("Developers=ralph@cs.arizona.edu") + write("Source Program=gif2wif.icn") + + write("[CONTENTS]") + write("Color Palette=yes") + write("Text=yes") + write("Weaving=yes") + write("Tieup=yes") + write("Color Table=yes") + write("Threading=yes") + write("Treadling=yes") + write("Warp colors=yes") + write("Weft colors=yes") + write("Warp=yes") + write("Weft=yes") + + write("[COLOR PALETTE]") + write("Entries=2") + write("Form=RGB") + write("Range=0," || 2 ^ 16 - 1) + + write("[TEXT]") + write("Title=example") + write("Author=Ralph E. Griswold") + write("Address=5302 E. 4th St., Tucson, AZ 85711") + write("EMail=ralph@cs.arizona.edu") + write("Telephone=520-881-1470") + write("FAX=520-325-3948") + + write("[WEAVING]") + write("Shafts=", *shafts) + write("Treadles=", *treadles) + write("Rising shed=yes") + + write("[WARP]") + write("Threads=", *threading) + write("Units=Decipoints") + write("Thickness=10") + write("Color=1") + + write("[WEFT]") + write("Threads=", *treadling) + write("Units=Decipoints") + write("Thickness=10") + write("Color=2") + + write("[COLOR TABLE]") + write("1=0,0,0") + write("2=65535,65535,65535") + + write("[THREADING]") + every i := 1 to *threading do + write(i, "=", threading[i]) + + write("[TREADLING]") + every i := 1 to *treadling do + write(i, "=", treadling[i]) + + write("[TIEUP]") + every i := 1 to *tieup do + write(i, "=", tromp(tieup[i])) + +end + +#procedure tromp(treadle) +# local result +# +# result := "" +# +# treadle ? { +# every result ||:= upto("1") || "," +# } +# +# return result[1:-1] +# +#end +# +procedure tromp(treadle) + local result, i + + result := "" + + every i := 1 to *treadle do + if treadle[i] == 1 then result ||:= i || "," + + return result[1:-1] + +end + +procedure examine(array) + local count, lines, line + + lines := table() # table to be keyed by line patterns + count := 0 + + every line := !array do # process lines + /lines[line] := (count +:= 1) # if new line, insert with new number + + return lines + +end + +procedure rot(rows) + local cols, row, grid, i + + cols := list(*rows[1], "") + + every row := !rows do { + i := 0 + every grid := !row do + cols[i +:= 1] := grid || cols[i] + } + + return cols + +end diff --git a/ipl/gprogs/gifs2pdb.icn b/ipl/gprogs/gifs2pdb.icn new file mode 100644 index 0000000..f84bf0d --- /dev/null +++ b/ipl/gprogs/gifs2pdb.icn @@ -0,0 +1,56 @@ +############################################################################ +# +# File: gifs2pdb.icn +# +# Subject: Program to produce custom palettes from GIF images +# +# Author: Ralph E. Griswold +# +# Date: April 13, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program makes a custom palette database from the colors in GIF +# images +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, palettes, wopen, xcode +# +############################################################################ + +link basename +link palettes +link wopen +link xcode + +global PDB_ + +procedure main(args) + local file, name, output, colors, win + + every file := !args do { + win := WOpen("image=" || file, "canvas=hidden") | { + write(&errout, "*** cannot open image: ", image(file)) + next + } + name := basename(file, ".gif") + colors := set() + every insert(colors, Pixel(win, 0, 0, WAttrib(win, "width"), + WAttrib(win, "height"))) + WClose(win) + makepalette(name, sort_colors(colors)) | + write(&errout, "*** cannot make palette from ", image(file)) + } + + xencode(PDB_, &output) + +end diff --git a/ipl/gprogs/giftoims.icn b/ipl/gprogs/giftoims.icn new file mode 100644 index 0000000..4440b5b --- /dev/null +++ b/ipl/gprogs/giftoims.icn @@ -0,0 +1,111 @@ +############################################################################ +# +# File: giftoims.icn +# +# Subject: Program to convert GIF files to image strings +# +# Author: Ralph E. Griswold +# +# Date: June 10, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts GIF images whose names are given on the command +# line to image strings as used by DrawImage(). +# +# The image strings are written to files with the basenames of the GIF +# files and the suffix "ims" or "iml" depending on the output option. +# +# The following options are supported: +# +# -l write Icon literal instead of plain string; suffix is +# .iml (default .ims). +# -i i make lines of literals at most i characters long +# -p s palette to use; default c1. +# +# For -l, the length refers to the number of characters represented. If +# they require escapes, thea actual line length will be longer. This is +# to prevent errors from trying to continue a string literal in the +# middle of an escape sequence. In addition, three blanks are prepended +# to each line and the characters # and $ are escaped to prevent then +# from being misinterpreted by Icon's translator. +# +# .iml files are suitable for inclusion in program text, either +# directly or by $include. +# +# .ims files are suitable for reading. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, graphics, options, strings +# +############################################################################ + +link basename +link graphics +link options +link strings + +procedure main(args) + local file, opts, name, output, literal, length, seg, palette, str + local suffix + + opts := options(args, "i+lp:") + + literal := opts["l"] + length := opts["i"] + palette := \opts["p"] | "c1" + + suffix := if \literal then ".iml" else ".ims" + + if not PaletteChars(palette) then + stop("*** invalid palette specification") + + every file := !args do { + name := basename(file, ".gif") || suffix + WOpen("canvas=hidden", "image=" || file) | { + write(&errout, "**** can't open ", file) + next + } + output := open(name, "w") | { + write(&errout, "*** can't write to ", name) + next + } + str := Capture(palette) + if /literal then writes(output, str) + else { + if /length then str ? { + length := integer(tab(upto(','))) + } + str ? { + write(output, " \"", tab(upto(',') + 1), tab(upto(',') + 1), "_") + while seg := move(length) do { + if pos(0) then write(output, " ", esc(seg), "\"") + else write(output, " ", esc(seg), "_") + } + if not pos(0) then write(output, " ", esc(tab(0)), "\"") + } + } + close(output) + WClose() + } + +end + +procedure esc(s) + + s := image(s) + s := replace(s, "$", "\\x24") + s := replace(s, "#", "\\x23") + + return s[2:-1] + +end diff --git a/ipl/gprogs/giftopat.icn b/ipl/gprogs/giftopat.icn new file mode 100644 index 0000000..86abe9d --- /dev/null +++ b/ipl/gprogs/giftopat.icn @@ -0,0 +1,46 @@ +############################################################################ +# +# File: giftopat.icn +# +# Subject: Program to convert GIF image to hex-form pattern +# +# Author: Ralph E. Griswold +# +# Date: May 29, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program convert GIF images, whose names are given on the command +# line to bi-level patterns. The GIFs are expected to be black and white. +# All non-white pixels are treated as black +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: imsutils, wopen +# +############################################################################ + +link imsutils +link wopen + +procedure main(args) + local file, win + + while file := get(args) do { + win := WOpen("image=" || file, "canvas=hidden") | { + write(&errout, "cannot open ", file) + next + } + write(pix2pat(win, 0, 0, WAttrib("width"), WAttrib("height"))) + WClose(win) + } + +end diff --git a/ipl/gprogs/gpxtest.icn b/ipl/gprogs/gpxtest.icn new file mode 100644 index 0000000..e8b8587 --- /dev/null +++ b/ipl/gprogs/gpxtest.icn @@ -0,0 +1,743 @@ +############################################################################ +# +# File: gpxtest.icn +# +# Subject: Program to test graphics procedures +# +# Author: Gregg M. Townsend +# +# Date: August 1, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program exercises a wide variety of graphics operations. Several +# independent output tests are run in square cells within a window. The +# resulting image can be compared with a standard image to determine its +# correctness. +# +# The "Dialog" button brings up an interactive dialog box test; the +# "Quit" button exits the program. +# +# Some variations among systems are expected in the areas of fonts, +# attribute values, and availability of mutable colors. The first test, +# involving window resizing, produces results that do not exactly fit the +# grid pattern of the other tests; that is also expected. +# +# This program is designed for a color display, but it also works on +# monochrome systems. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, dsetup, evmux, graphics +# +############################################################################ + +link button +link dsetup +link evmux +link graphics + + +$define CELL 80 # size of one test "cell" +$define HALF (CELL / 2) # half a cell +$define GAP 10 # gap between cells + +$define NWIDE 6 # number of cells across +$define NHIGH 4 # number of cells down + +$define WIDTH (NWIDE * (CELL + GAP)) # total width +$define HEIGHT (NHIGH * (CELL + GAP)) # total height + +$define ABET "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" + + +global cx, cy # current cell indices + + +############################## Overall control ############################## + + +procedure main(args) + local x, y + + # Start with a medium window; shrink, test defaults, grow. + Window("size=300,300", "bg=light-weak-reddish-yellow", args) + VSetFont() + + # The following sequence *should* have no permanent effect + WAttrib("drawop=xor", "fillstyle=masked", "pattern=checkers", "linewidth=5") + DrawCircle(CELL / 2, CELL / 2, CELL / 3) + EraseArea() + WAttrib("drawop=copy", "fillstyle=solid", "linewidth=1") + + # Shrink the window, test defaults, grow to final size. + deftest() + WAttrib("size=" || WIDTH || "," || HEIGHT) + WAttrib("width=" || WIDTH) # should be no-op + WAttrib("size=" || WIDTH || "," || HEIGHT) # should be no-op + + # Make a simple background. + if WAttrib("depth") > 1 then + Fg("44000,39000,24000") + every y := (3 * CELL / 2) to (2 * HEIGHT) by 7 do + DrawLine(0, y, 2 * y, 0) + Fg("#000") + + # Run a series of tests confined to small, square cells. + cx := cy := 0 # current cell (already filled) + cell(simple) + cell(lines) + cell(rects) + cell(star) + cell(pretzel) + cell(spiral) + cell(arcs) + cell(copying) + cell(rings) + cell(fontvars) + cell(stdfonts) + cell(stdpats) + cell(patts) + cell(attribs) + cell(gamma) + cell(balls) + cell(slices) + cell(details) + cell(rainbow) + cell(whale) + cell(cheshire) + + # Use the final cell area for Dialog and Quit buttons. + buttonrow(&window, WIDTH - CELL - GAP/2, HEIGHT - GAP / 2, CELL, 2 * GAP, + 0, - 3 * GAP, "Quit", argless, exit, "Dialog", argless, dltest) + quitsensor(&window) + sensor(&window, 'Dd', argless, dltest) + evmux(&window) +end + + +## cell(proc) -- run a test in the next available cell +# +# Proc is called with a private graphics context assigned to &window. +# Clipping set to cell boundaries and the origin is at the center. + +procedure cell(proc) + local x, y, stdwin + + if (cx +:= 1) >= NWIDE then { + cx := 0 + cy +:= 1 + } + x := integer((cx + .5) * (CELL + GAP)) + y := integer((cy + .5) * (CELL + GAP)) + + stdwin := &window + &window := Clone("dx=" || x, "dy=" || y, "bg=white") + ClearOutline(-HALF - 1, -HALF - 1, CELL + 1, CELL + 1) + Clip(-HALF, -HALF, CELL, CELL) + proc() + Uncouple(&window) + &window := stdwin +end + + +############################## Cell Tests ############################## + + +## arcs() -- draw a series of arcs forming a tight spiral +# +# Tests DrawCircle with angle limits. + +procedure arcs() + local r, a, d + + r := 2 + a := 0 + d := &pi / 10 + while r < HALF do { + DrawCircle(0, 0, r, a, d) + r +:= 1 + a +:= d + d +:= &pi / 40 + } +end + + +## attribs() -- test WAttrib(). +# +# For each of several attributes we should be able to inquire the current +# setting, set it to that value, and get it back again. If that works, +# display some system-dependent attributes in the cell window. + +procedure attribs() + local alist, afail, n, a, f, cw, ch, cl, v1, v2 + + alist := [ + "fg", "bg", "reverse", "drawop", "gamma", "font", "leading", + "linewidth", "linestyle", "fillstyle", "pattern", + "clipx", "clipy", "clipw", "cliph", "dx", "dy", + "label", "pos", "posx", "posy", "size", "height", "width", "canvas", + "resize", "echo", "cursor", "x", "y", "row", "col", "pointer", + "pointerx", "pointery", "pointerrow", "pointercol", + ] + afail := [] + + every a := \!alist do { + v1 := WAttrib(a) | { put(afail, a); next } + WAttrib(a || "=" || v1) | { put(afail, a || "=" || v1); next } + v2 := WAttrib(a) | { put(afail, a); next } + v1 == v2 | { put(afail, a || ": " || v1 || "/" || v2); next } + } + + Translate(-HALF, -HALF) + GotoRC(1, 1) + + if *afail > 0 then { + Font("sans,bold,10") + WWrite("FAILED:") + every WWrite(" ", !afail) + every write(&errout, "WAttrib() failure: ", !afail) + fail + } + + f := WAttrib("font") | "[FAILED]" + cw := WAttrib("fwidth") | "[FAILED]" + ch := WAttrib("fheight") | "[FAILED]" + cl := WAttrib("leading") | "[FAILED]" + Font("sans,10") + WWrite("display=", WAttrib("display") | "[FAILED]") + WWrite(" (", WAttrib("displaywidth") | "????", "x", + WAttrib("displayheight") | "????", "x", WAttrib("depth") | "??", ")") + every a := "gamma" | "pointer" do + WWrite(a, "=", WAttrib(a) | "[FAILED]") + WWrite("vfont=", f) + WWrite(" (", cw, "x", ch, ", +", cl, ")") +end + + +## balls() -- draw a grid of spheres +# +# Tests DrawImage using g16 palette. + +procedure balls() + every DrawImage(-HALF + 2 to HALF by 20, -HALF + 2 to HALF by 20, + " 16 , g16 , FFFFB98788AEFFFF_ + FFD865554446AFFF FD856886544339FF E8579BA9643323AF_ + A569DECA7433215E 7569CDB86433211A 5579AA9643222108_ + 4456776533221007 4444443332210007 4333333222100008_ + 533322221100000A 822222111000003D D41111100000019F_ + FA200000000018EF FFA4000000028EFF FFFD9532248BFFFF") +end + + +## cheshire() -- cheshire cat display +# +# Tests mutable colors, WDelay, various drawing operations. + +procedure cheshire() + local face, eyes, grin, i, g + + if (face := NewColor("white")) & + (eyes := NewColor("black")) & (grin := NewColor("black")) then { + Fg("gray") + FillRectangle(-HALF, -HALF) + Fg(face) + FillArc(-HALF, .3 * CELL, CELL, -HALF) + FillPolygon(0, 0, -.35 * CELL, -.35 * CELL, -.35 * CELL, 0) + FillPolygon(0, 0, .35 * CELL, -.35 * CELL, .35 * CELL, 0) + Fg(eyes) + WAttrib("linewidth=2") + DrawCircle(-.18 * CELL, -.0 * CELL, 3, , , .18 * CELL, -.0 * CELL, 3) + Fg(grin) + DrawCircle(0, -HALF, .7 * CELL, &pi / 3, &pi / 3) + WDelay(500) + every i := 0 to 30 by 2 do { + WDelay(100) + g := i * 65535 / 60 + Color(eyes, g || "," || g || "," || g) + g := 65535 - g + Color(face, g || "," || g || "," || g) + } + every i := 0 to 26 by 2 do { + WDelay(100) + g := i * 65535 / 60 + Color(grin, g || "," || g || "," || g) + } + } + else { + Translate(-HALF + 4, -HALF) + GotoRC(1, 1) + WWrite("this test\nrequires\nmutable\ncolors") + } +end + + +## copying() -- test CopyArea +# +# Tests hidden canvas, overlapping copies, and generation +# of background color for missing source pixels. + +procedure copying() + local win, o, w, h + + win := WOpen("canvas=hidden", "size=" || CELL || "," || CELL) | { + GotoRC(1, 1) + WWrite("Can't get\nhidden\ncanvas") + fail + } + every DrawCircle(win, HALF, HALF, HALF - 2 to sqrt(2) * HALF by 3) + + o := 5 # offset for copy + w := CELL / 4 # width of square to be copied + h := w / 2 # half of that, for centering + Bg(win, "black") + + CopyArea(win, -o, -o, w, w, 0, 0) + CopyArea(win, HALF - h, -o, w, w, HALF - h, 0) + CopyArea(win, CELL + o, -o, -w, w, CELL - w, 0) + + CopyArea(win, -o, HALF - h, w, w, 0, HALF - h) + CopyArea(win, CELL + o, HALF - h, -w, w, CELL - w, HALF - h) + + CopyArea(win, -o, CELL + o, w, -w, 0, CELL - w) + CopyArea(win, HALF - h, CELL + o, w, -w, HALF - h, CELL - w) + CopyArea(win, CELL + o, CELL + o, -w, -w, CELL - w, CELL - w) + + CopyArea(win, o, o, w, w, HALF - w, HALF - w) + CopyArea(win, CELL - o, o, -w, w, HALF, HALF - w) + CopyArea(win, o, CELL - o, w, -w, HALF - w, HALF) + CopyArea(win, CELL - o, CELL - o, -w, -w, HALF, HALF) + + CopyArea(win, &window, , , , , -HALF, -HALF) + close(win) +end + + +## deftest() -- test defaults +# +# Tests x/y/w/h defaulting by adjusting the window size several times. +# Also exercises "drawop=reverse" incidentally. +# +# This test must be run first. It uses the entire window and leaves +# results in the first cell. + +procedure deftest() + WAttrib("drawop=reverse") + WAttrib("size=" || CELL || "," || CELL / 2) + FillArc() + FillArc(, , CELL / 4) + FillArc(3 * CELL / 4) + WAttrib("height=" || CELL) + DrawArc(, CELL / 2) + WAttrib("drawop=copy") +end + + +## details() -- test drawing details +# +# Tests some of the details of filling and stroking. + +procedure details() + Shade("light gray") + FillRectangle() + + WAttrib("linewidth=7", "fg=white") + DrawLine(10, 10, 10, 25, 30, 25, 20, 10) + WAttrib("linewidth=1", "fg=black") + DrawLine(10, 10, 10, 25, 30, 25, 20, 10) + + Fg("white") + DrawRectangle(-5, -5, -25, -30) + Fg("black") + DrawArc(-5, -5, -25, -30) + + Fg("white") + FillArc(5, -5, 24, -30) + Fg("black") + DrawArc(5, -5, 24, -30) + + Shade("light gray") + FillCircle(17, -17, 6) + Fg("black") + DrawCircle(17, -17, 6) + + Fg("white") + FillPolygon(-5,20, -17,23, -20,35, -23,23, -35,20, -23,17, -20,5, -17,17) + Fg("black") + DrawPolygon(-5,20, -17,23, -20,35, -23,23, -35,20, -23,17, -20,5, -17,17) +end + + +## fontvars() -- test font variations +# +# Tests various font characteristics combined with standard font names. +# Also exercises Shade, GoToXY, WWrites. + +procedure fontvars() + Translate(-HALF + 4, -HALF) + Shade("gray") + FillRectangle(-4) + Shade("black") + GotoXY(0, 0) + WWrites("\nFonts...") + WWrites("\n", if Font("mono,12") then ABET else "no mono 12") + WWrites("\n", if Font("serif,italic") then ABET else "no SF ital") + WWrites("\n", if Font("sans,bold,18") then ABET else "no SN B 18") + WWrites("\n", if Font("fixed") then ABET else "no fixed!") +end + + +## gamma() -- test gamma correction +# +# Draws 50%-gray bars with various values of the gamma attribute, beginning +# with the system default. Incidentally tests some font attributes. + +procedure gamma() + local g + + GotoXY(0, -HALF + WAttrib("leading") - WAttrib("descent")) + every g := &null | 1.0 | 1.5 | 2.2 | 3.3 | 5.0 | 7.5 do { + Shade("gray") + WAttrib("gamma=" || \g) + FillRectangle(-4, WAttrib("y") + WAttrib("descent"), + -HALF, -WAttrib("leading")) + Shade("black") + WWrite(WAttrib("gamma")) + } +end + + +## lines() -- test line drawing +# +# Tests proper drawing and joining of lines of various widths. There +# once were problems here in Icon, and there still are in some X servers. + +procedure lines() + local i, y + y := -HALF - 6 + every WAttrib("linewidth=" || (0 to 4)) do + tline(-HALF + 10, y +:= 15) +end + +procedure tline(x, y) + DrawLine(x + 1, y, x + 3, y) + DrawLine(x - 1, y, x - 3, y) + DrawLine(x, y + 1, x, y + 3) + DrawLine(x, y - 1, x, y - 3) + x +:= 15 + DrawLine(x - 3, y - 3, x + 3, y - 3) + DrawLine(x + 3, y - 3, x + 3, y + 3) + DrawLine(x + 3, y + 3, x - 3, y + 3) + DrawLine(x - 3, y + 3, x - 3, y - 3) + x +:= 15 + DrawLine(x - 3, y - 3, x + 3, y + 3) + DrawLine(x - 3, y + 3, x + 3, y - 3) + x +:= 15 + DrawLine(x, y - 4, x + 4, y) + DrawLine(x + 4, y, x, y + 4) + DrawLine(x, y + 4, x - 4, y) + DrawLine(x - 4, y, x, y - 4) + x +:= 15 + DrawRectangle(x - 4, y - 4, 8, 8) +end + + +## patts() -- test custom patterns +# +# Tests custom patterns in hex and decimal forms; tests fillstyle=masked. + +procedure patts() + local i, j, s, w + + WAttrib("linewidth=4") + DrawCircle(0, 0, 0.38 * CELL) # circle should persist after patts + WAttrib("linewidth=1") + Translate(-HALF, -HALF) + w := (CELL + 2) / 3; + + WAttrib("fillstyle=masked") + s := ["8,#01552B552B552BFF", "8,#020E070420E07040", + "8,31,14,68,224,241,224,68,14", "8,#2020FF020202FF20", "4,#5A5A", + "8,#0ABBA0BE82BAAAEA", "8,#E3773E383E77E383", "8,#4545C71154547C11", + "8,#FF7F3F1F0F070301"] + + every i := 0 to 2 do + every j := 0 to 2 do { + WAttrib("pattern=" || s[3 * i + j + 1]) + FillRectangle(w * j, w * i, w, w) + } +end + + +## pretzel() -- draw a pretzel +# +# Tests DrawCurve. + +procedure pretzel() + WAttrib("linewidth=3") + DrawCurve(20, -20, -5, 0, 20, 20, 35, 0, 0, + -20, -35, 0, -20, 20, 5, 0, -20, -20) +end + + +## rainbow() -- draw a rainbow +# +# Tests several color naming variations. + +procedure rainbow() + local r, c, l + + Shade("moderate blue-cyan") + FillRectangle() + WAttrib("fillstyle=solid") + r := 20 + l := ["pink", "pale orange", "light yellow", "pale green", "pale blue", + "light bluish violet", " pale violet"] + WAttrib("linewidth=3") + every Fg(!l) do + DrawCircle(0, 20, r +:= 3, 0, -&pi) +end + + +## rects() -- draw rectangles +# +# Tests rectangles specified with positive & negative width & height. + +procedure rects() + local r, a + + WAttrib("drawop=reverse") + r := HALF + every a := 1 to 19 by 2 do + DrawRectangle(0, 0, r * cos(0.33 * a), r * sin(0.33 * a)) +end + + +## rings() -- draw a pile of rings +# +# Tests linewidth and DrawCircle in combination. + +procedure rings() + local x, y + Translate(-HALF, -HALF) + FillRectangle() + every 1 to 15 do { + x := ?CELL + y := ?CELL + WAttrib("fg=black", "linewidth=5") + DrawCircle(x, y, 30) # draw ring in black + WAttrib("fg=white", "linewidth=3") + DrawCircle(x, y, 30) # color with white band + } +end + + +## simple() -- an easy first test +# +# Tests DrawString, DrawCircle, FillRectangle, EraseArea, linestyles. + +procedure simple() + DrawCircle(0, 0, CELL / 3) + DrawString(-HALF + 4, -HALF + 12, "hello,") + DrawString(-HALF + 4, -HALF + 25, "world") + FillRectangle(0, 0) + EraseArea(10, 4, CELL / 5, CELL / 3) + WAttrib("linestyle=dashed") + DrawLine(HALF - 3, HALF, HALF - 3, -HALF) + WAttrib("linestyle=striped") + DrawLine(HALF - 6, HALF, HALF - 6, -HALF) +end + + +## slices() -- draw a pie with different-colored slices +# +# Tests RandomColor, Shade, FillArc. + +procedure slices() + local n, a, da, ov + + n := 10 + da := 2 * &pi / n # change in angle + a := -&pi / 2 - da # current angle + ov := &pi / 1000 # small overlap + + FillRectangle(-HALF, -HALF) + every 1 to n do { + Shade(RandomColor()) + FillArc(-HALF, -CELL / 3, CELL, 2 * CELL / 3, a +:= da, da + ov) + } +end + + +## spiral() -- draw a spiral, one point at a time +# +# Tests DrawPoint. + +procedure spiral() + local r, a, d + + r := 3 # initial radius + a := 0 # initial start angle + while r < HALF do { + DrawPoint(r * cos(a), r * sin(a)) + d := 1.0 / r + a +:= d + r +:= 2 * d + } +end + + +## star() -- draw a five-pointed star. +# +# Tests FillPolygon and the even-odd winding rule. + +procedure star() + FillPolygon(-40, -10, 40, -10, -25, 40, 0, -40, 25, 40) +end + + + +## stdfonts() -- test standard fonts +# +# Shows the default font (the header line), standard fonts, and "fixed". + +procedure stdfonts() + Translate(-HALF + 4, -HALF) + Shade("gray") + FillRectangle(-4) + Shade("black") + GotoRC(1, 1) + WWrite(if Font("mono") then "mono" else "no mono!") + WWrite(if Font("typewriter") then "typewriter" else "no typewriter!") + WWrite(if Font("sans") then "sans" else "no sans!") + WWrite(if Font("serif") then "serif" else "no serif!") + WWrite(if Font("fixed") then "fixed" else "no fixed!") +end + + +## stdpats() -- test standard patterns +# +# Tests standard pattern names; tests fillstyle=textured. + +procedure stdpats() + local i, j, s, x, y + + WAttrib("fillstyle=textured") + s := [ + "black", "verydark", "darkgray", "gray", "lightgray", "verylight", + "white", "vertical", "diagonal", "horizontal", "grid", "trellis", + "checkers", "grains", "scales", "waves"] + every i := 0 to 3 do + every j := 0 to 3 do { + WAttrib("pattern=" || s[4 * i + j + 1]) + x := -HALF + j * CELL / 4 + y := -HALF + i * CELL / 4 + FillRectangle(x, y) # depends on opacity of patterns to work + } +end + + +## whale() -- draw a whale +# +# Tests transparent and regular images, Capture, Zoom. + +procedure whale() + local s + + Fg("moderate greenish cyan") + FillRectangle() + Translate(-HALF, -HALF) + + DrawImage(3, 3, "32, c1, _ + ~~~~~~~~~~~~000~~~~~~00~~~~~~~00_ + ~~~~~~~~~~~0JJJ00~~~~0J00~~~00J0_ + ~~~~~~~000000JJJJ0~~~0J0J000J0J0_ + ~~~~~000iiiii000JJ0~~0JJJ0J0JJi0_ + ~~~~06660ii000ii00J0~~00JJJJJ00~_ + ~~~066000i06600iii00~~~~0iii0~~~_ + ~~0066000i06000iiii0~~~~~0i0~~~~_ + ~~0i0000iii000iiiiii0~~~~0i0~~~~_ + ~0iiiiiiiiiiiiiiiiiii0~~0ii0~~~~_ + ~00000iii0000iiiiiiiii00iiii0~~~_ + 0AAAAA000AAAA00iiiiiiiiiiiii0~~~_ + 0AAAAAAAAAAAAAA0iiiiiiiiiiii0~~~_ + ~0000AAAAA0000AA0iiiiiiiiiiii0~~_ + ~06060000060600AA0iiiiiiiiiii0~~_ + ~060606060606000A0iiiii00iiii0~~_ + ~~0~006060000000AA0iiiiiJ0iii0~~_ + ~~~~~~00000000000A0iiii0JJ0ii0~~_ + ~~~~~~00000000000A0iiiiJ0J0ii0~~_ + ~~~0~~00000000000A0iii0JJ00i0~~~_ + ~~060000000000000A0i0JJ0JJ0i0~~~_ + ~~06060600000600AA0ii0JJ00ii0~~~_ + ~00006060606060AA0iiii000ii0~~~~_ + 0AAA0000060600AAA0iiiiiiiii0~~~~_ + 0AAAAAAAA000AAAA0iiiiiiiiii0~~~~_ + ~000AAAAAAAAAAA0iiiiiiiiii0~~~~~_ + ~~0i0000AAAAA00iiiiiiiiiii0~~~~~_ + ~~0iiiii00000iiiiiiiiiiii0~~~~~~_ + ~~~0iiiiiiiiiiiiiiiiiiii0~~~~~~~_ + ~~~~0iiiiiiiiiiiiiiiii00~~~~~~~~_ + ~~~~~00iiiiiiiiiiiii00~~~~~~~~~~_ + ~~~~~~~000iiiiiii000~~~~~~~~~~~~_ + ~~~~~~~~~~0000000~~~~~~~~~~~~~~~") + + s := Capture(, 0, 0, 36, 36) + DrawImage(0, 40, s) + + Zoom(0, 0, 36, 36, 40, 20, 72, 72) +end + + +############################## Dialog test ############################## + + +## dltest() -- dialog test +# +# Present a dialog box with "Validate" and "Cancel" buttons. +# For "Validate", check all values, and repeat dialog if incorrect. +# For "Cancel", return immediately. + +procedure dltest() + while dlog() ~== "Cancel" do { + if dialog_value["button"] ~=== 1 then + { Notice("The button was not left dark."); next } + if dialog_value["xbox"] ~=== 1 then + { Notice("The checkbox was not checked."); next } + if dialog_value["slider"] < 0.8 then + { Notice("The slider was not set."); next } + if map(dialog_value["text"]) ~== "icon" then + { Notice("The text did not say `Icon'"); next } + Notice("All values were correct.") + return + } +end + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure dlog(win, deftbl) +static dstate +initial dstate := dsetup(win, + ["dlog:Sizer::1:0,0,370,220:",], + ["button:Button:regular:1:291,21,56,21:button",], + ["cancel:Button:regular::198,174,100,30:Cancel",], + ["label1:Label:::20,25,252,13:Click this button and leave it dark:",], + ["label2:Label:::20,55,105,13:Check this box:",], + ["label3:Label:::20,85,238,13:Move this slider to the far right:",], + ["rule:Line:::20,157,350,157:",], + ["slider:Slider:h::273,86,76,15:0.0,1.0,0.5",], + ["text:Text::6:20,115,214,17:Enter the word `Icon': \\=here",], + ["validate:Button:regular:-1:75,174,100,30:Validate",], + ["xbox:Button:xbox:1:131,54,16,16:",], + ) +return dpopup(win, deftbl, dstate) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/gridedit.icn b/ipl/gprogs/gridedit.icn new file mode 100644 index 0000000..71706dd --- /dev/null +++ b/ipl/gprogs/gridedit.icn @@ -0,0 +1,56 @@ +############################################################################ +# +# File: gridedit.icn +# +# Subject: Program to create and edit binary arrays +# +# Author: Ralph E. Griswold +# +# Date: December 21, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This application provides a variety of facilities for creating and +# editing binary arrays. It is intended for use with weaving tie-ups +# and liftplans. +# +############################################################################ +# +# Requires: Version 9 graphics, /tmp +# +############################################################################ +# +# Links: tieedit +# +############################################################################ + +link tieedit + +procedure main(args) + + grid_init() + + ready() + + while ProcessEvent(grid_root, , grid_shortcuts) do + if \grid_state then exit() + +end + +procedure ready() + + grid_state := &null + + grid_rows := pat2rows("10,#001002004008010020040080100200") + + setup() + + WAttrib(grid_window, "canvas=normal") + + return + +end diff --git a/ipl/gprogs/gxplor.icn b/ipl/gprogs/gxplor.icn new file mode 100644 index 0000000..3e331aa --- /dev/null +++ b/ipl/gprogs/gxplor.icn @@ -0,0 +1,380 @@ +############################################################################ +# +# File: gxplor.icn +# +# Subject: Program to explore graphics facilities +# +# Author: Gregg M. Townsend +# +# Date: July 20, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: gxplor [-s] [window options] +# +# gxplor is an interactive explorer for experimenting with Icon's +# graphics facilities. Commands read from standard input set window +# attributes or invoke procedures. Result values are reported on +# standard output. Errors are caught when possible. +# +# Here's an example, with commentary at the side, that illustrates +# some of the possibilities: +# +# % gxplor start program; a window appears +# > fg query value of "fg" attribute +# black +# > fg blue set "fg" attribute +# blue +# > linewidth 7 set "linewidth" attribute +# 7 +# > drawline 12 20 55 73 a fat blue line appears +# > erasearea clear window +# > fillarea +# [unrecognized] oops -- wrong name +# > fillrectangle +# > pattern query "pattern" attribute +# [failed] +# > pattern grid set it +# grid +# > fillstyle +# solid +# > fillstyle opaque +# error 205: invalid value +# > fillstyle textured set fillstyle +# textured +# > clip 50 50 400 200 set clipping +# > fillrectangle fill clipped area with pattern +# > zoom 40 40 100 100 300 50 200 200 +# zoom a region +# > &storage query memory usage +# 0 +# 274 +# 12184 +# > exit exit the program +# % +# +# Input consists of blank-separated words, as shown. If the first +# word is recognized as the name of an attribute, a WAttrib() call is +# made. If it is an Icon keyword, the keyword value is printed. +# Otherwise, the word is treated as a procedure name. Any built-in +# function or linked procedure can be invoked, and procedure names are +# treated as case-insensitive for ease of entry. +# +# If a line begins with an integer, the remainder of the line is +# interpreted as a command to be repeated that number of times. +# Afterwards, the elapsed CPU and wall-clock time is reported; +# these figures include loop and call overhead. +# +# The -s option selects "script" mode: input is echoed on standard +# output, and at EOF the program pauses in WDone(). +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: many +# +############################################################################ + +link graphics +link options +link datetime +link random + +link barchart, bitplane, drawcard, decay, imscolor +link psrecord, putpixel, strpchrt, turtle, vsetup + +invocable all + +$define MaxErrors 100 + + +# main procedure + +procedure main(args) + local line, words, n, tm, ck, verb, p, s, w, r, atts, keywds, opts + + atts := attnames() + keywds := kwnames() + Window(args) + opts := options(args, "s") + + repeat { + + # read next line + writes("> ") + line := read() | break + if \opts["s"] then + write(line) + words := crack(line) + + # set up for timing, if wanted + if n := integer(words[1]) then { + get(words) + tm := &time + ck := &clock + } + else { + n := 1 + tm := ck := &null + } + + verb := get(words) | next + &error := MaxErrors + + if member(atts, verb) then { + + # attribute name + s := verb || "=" + every w := !words do + s ||:= (\w | "") || " " + s := trim(s, ' =') + every 2 to n do + WAttrib(s) + r := image(WAttrib(s)) | "[failed]" + } + + else if member(keywds, verb) then { + + # keyword name + every kwval(verb, n - 1) + s := &null + every write(s := image(kwval(verb, 1))) + if /s then write("[failed]") + r := "window" # inhibit later result printing + } + + else if p := getproc(verb) then { + + # procedure call + dialog_value := &null + every 2 to n do + p ! words + r := image(p ! words) | "[failed]" + r ||:= " (dialog_value = " || image(\dialog_value) || ")" + } + + else { + + r := "[unrecognized]" + tm := ck := &null + } + + # calculate elapsed time + if \tm then { + WSync() + tm := &time - tm + ck := clkdiff(&clock, ck) + } + + # report result or error + if &error = MaxErrors then { + # no error occurred + if not (r ? ="window") then + write(r) + } + else if &error = MaxErrors - 1 then { + # an error occurred + write("error ", &errornumber, ": ", &errortext) + write("offending value: ", &errorvalue) + } + else { + # error conversion led to a second error; + # original information has been lost + write("error (details lost)") + } + + # write timing results + write("n=", n, " time=", \tm / 1000.0, " clock=", \ck) + + &error := 0 + } + + # at EOF, if called with -s option, wait for "Q" in window + if \opts["s"] then { + write("EOF") + WDone() + } +end + + + +# crack(s) -- parse line, returning list of words + +procedure crack(s) + local words + + words := [] + s ? { + tab(many(' \t')) + while not pos(0) do { + put(words, tab(upto(' \t') | 0)) + tab(many(' \t')) + } + } + return words +end + + + +# getproc(s) -- get procedure named s, case insensitive + +procedure getproc(s) + local p, f, line, tname + static proctab + + initial { + # put every builtin function in the table + proctab := table() + every p := function() do + proctab[map(p)] := proc(p) + + # open a temporary file to get procedure names + randomize() + tname := "gxp" || right(?99999, 5, "0") || ".tmp" +$ifdef _UNIX + tname := "/tmp/" || tname +$endif + f := open(tname, "crw") | stop("can't open ", tname) + + # put every linked procedure in the table + display(0, f) + seek(f, 1) + while line := read(f) do line ? { + tab(upto('=')) | next + tab(many('= ')) + ="procedure" | next + tab(many(' ')) + p := trim(tab(0)) + proctab[map(p)] := proc(p) + } + close(f) + remove(tname) + } + + return \proctab[map(s)] +end + + + +# attnames() -- return set of known attribute names + +procedure attnames() + return set([ + "ascent", "bg", "canvas", "ceol", "cliph", "clipw", "clipx", "clipy", + "col", "columns", "cursor", "depth", "descent", "display", + "displayheight", "displaywidth", "drawop", "dx", "dy", "echo", "fg", + "fheight", "fillstyle", "font", "fwidth", "gamma", "geometry", "height", + "iconic", "iconimage", "iconlabel", "iconpos", "image", "label", + "leading", "lines", "linestyle", "linewidth", "pattern", "pointer", + "pointercol", "pointerrow", "pointerx", "pointery", "pos", "posx", + "posy", "resize", "reverse", "row", "rows", "size", "visual", "width", + "windowlabel", "x", "y"]) +end + + + +# kwnames() -- return set of known keyword names + +procedure kwnames() + return set([ + "&allocated", "&ascii", "&clock", "&col", "&collections", + "&control", "&cset", "¤t", "&date", "&dateline", "&digits", + "&dump", "&e", "&error", "&errornumber", "&errortext", "&errorvalue", + "&errout", "&fail", + "&features", "&file", "&host", "&input", "&interval", "&lcase", "&ldrag", + "&letters", "&level", "&line", "&lpress", "&lrelease", "&main", "&mdrag", + "&meta", "&mpress", "&mrelease", "&null", "&output", "&phi", "&pi", + "&pos", "&progname", "&random", "&rdrag", "®ions", "&resize", "&row", + "&rpress", "&rrelease", "&shift", "&source", "&storage", "&subject", + "&time", "&trace", "&ucase", "&version", "&window", "&x", "&y"]) +end + + + +# kwval(name, n) -- generate values of a keyword n times + +procedure kwval(name, n) + case name of { + "&allocated": every 1 to n do suspend &allocated + "&ascii": every 1 to n do suspend &ascii + "&clock": every 1 to n do suspend &clock + "&col": every 1 to n do suspend &col + "&collections": every 1 to n do suspend &collections + "&control": every 1 to n do suspend &control + "&cset": every 1 to n do suspend &cset + "¤t": every 1 to n do suspend ¤t + "&date": every 1 to n do suspend &date + "&dateline": every 1 to n do suspend &dateline + "&digits": every 1 to n do suspend &digits + "&dump": every 1 to n do suspend &dump + "&e": every 1 to n do suspend &e + "&error": every 1 to n do suspend &error + "&errornumber": every 1 to n do suspend &errornumber + "&errortext": every 1 to n do suspend &errortext + "&errorvalue": every 1 to n do suspend &errorvalue + "&errout": every 1 to n do suspend &errout + "&fail": every 1 to n do suspend &fail + "&features": every 1 to n do suspend &features + "&file": every 1 to n do suspend &file + "&host": every 1 to n do suspend &host + "&input": every 1 to n do suspend &input + "&interval": every 1 to n do suspend &interval + "&lcase": every 1 to n do suspend &lcase + "&ldrag": every 1 to n do suspend &ldrag + "&letters": every 1 to n do suspend &letters + "&level": every 1 to n do suspend &level + "&line": every 1 to n do suspend &line + "&lpress": every 1 to n do suspend &lpress + "&lrelease": every 1 to n do suspend &lrelease + "&main": every 1 to n do suspend &main + "&mdrag": every 1 to n do suspend &mdrag + "&meta": every 1 to n do suspend &meta + "&mpress": every 1 to n do suspend &mpress + "&mrelease": every 1 to n do suspend &mrelease + "&null": every 1 to n do suspend &null + "&output": every 1 to n do suspend &output + "&phi": every 1 to n do suspend &phi + "&pi": every 1 to n do suspend &pi + "&pos": every 1 to n do suspend &pos + "&progname": every 1 to n do suspend &progname + "&random": every 1 to n do suspend &random + "&rdrag": every 1 to n do suspend &rdrag + "®ions": every 1 to n do suspend ®ions + "&resize": every 1 to n do suspend &resize + "&row": every 1 to n do suspend &row + "&rpress": every 1 to n do suspend &rpress + "&rrelease": every 1 to n do suspend &rrelease + "&shift": every 1 to n do suspend &shift + "&source": every 1 to n do suspend &source + "&storage": every 1 to n do suspend &storage + "&subject": every 1 to n do suspend &subject + "&time": every 1 to n do suspend &time + "&trace": every 1 to n do suspend &trace + "&ucase": every 1 to n do suspend &ucase + "&version": every 1 to n do suspend &version + "&window": every 1 to n do suspend &window + "&x": every 1 to n do suspend &x + "&y": every 1 to n do suspend &y + } +end + + + +# clkdiff(a, b) -- return difference in seconds between two &clock values +# +# If a < b, the time is assumed to have wrapped past midnight. + +procedure clkdiff(a, b) + local t + t := ClockToSec(a) - ClockToSec(b) + if t < 0 then + t +:= ClockToSec("24:00:00") + return t +end diff --git a/ipl/gprogs/hb.icn b/ipl/gprogs/hb.icn new file mode 100644 index 0000000..077c3c2 --- /dev/null +++ b/ipl/gprogs/hb.icn @@ -0,0 +1,334 @@ +############################################################################ +# +# File: hb.icn +# +# Subject: Program for Hearts & Bones game +# +# Author: Robert J. Alexander +# +# Date: March 10, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Hearts & Bones +# +# Usage: hb [-h <board height>] [-w <board width>] [-b <# bones>] [-B] +# +# -B says to print the actual number of bones placed. +# +# For best results, use odd board heights and widths, and even +# square heights and widths. +# +# Defaults: board height = 9, board width = 13, # bones = 25. +# +# --- Game Play --- +# +# Hit "q" to quit, "r" to start a new game. +# +# The object is to visit all the safe squares without stepping on a +# bone. +# +# You *visit* a square by clicking the left mouse button in it. If the +# square is safe, a number is posted in it that reveals the number of +# squares in the eight neighboring squares the contain bones. Squares +# containing hearts (represented by $) are always safe. +# +# You can only visit squares that are adjacent to squares already +# visited. At the start of a game, the upper left square (a heart +# square) is pre-visited for you. If a visited square has no +# neighbors, its adjacent squares are automatically visited for you, as +# a convenience. +# +# At any time you can *mark* a square that you believe has a bone by +# clicking the right (or center) mouse button on it. This is a memory +# aid only -- if you visit it later (and you were right), you're dead. +# There is no confirmation whether a square you have marked really +# contains a bone, although you will probably find out later when it +# causes you to make a mistake. A right-button click on a marked +# square unmarks it. +# +# The game ends when you have visited all safe squares or stepped on a +# bone. (Presently, there is no automatic detection of a winning board +# -- you just have to notice that for yourself). +# +# NOTE: If you use the command line options to alter the setup +# parameters (e.g. increasing the number of squares, or *decreasing* +# the number of bones), you might get a stack overflow due, I think, to +# deep recursion. I have found that setting the environment variable +# MSTKSIZE=30000 works well. +# +############################################################################ +# +# Links: options, random, wopen +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ + +link options +link random +link wopen + +global height, width, nbr_bones, x1, y1, sq, print_bone_count + + +procedure main(arg) + initialize(arg) + play() + return +end + + +procedure draw_board(win) + local x, y, x2, y2 + x2 := x1 + width * sq + y2 := y1 + height * sq + x := x1 + every 1 to width + 1 do { + DrawLine(win, x, y1, x, y2) + x +:= sq + } + y := y1 + every 1 to height + 1 do { + DrawLine(win, x1, y, x2, y) + y +:= sq + } + return +end + + +procedure set_up_board(win, visited) + local board, pt + EraseArea(win) + board := make_board() + set_bones(board, nbr_bones) + calc_neighbors(board) + draw_board(win) + draw_hearts(win) + every pt := spread_zeros(board, 1, 1) do { + write_to_square(win, pt[1], pt[2], pt[3]) + visited[pt[1], pt[2]] := 1 + } + return board +end + + +procedure draw_hearts(win) + local pt + every pt := generate_heart_squares() do + write_to_square(win, pt[1], pt[2], "$") + return +end + + +procedure legal_move(x, y, visited) + local xx, yy + every xx := x - 1 to x + 1 & yy := y - 1 to y + 1 do + if \visited[xx, yy] then { + visited[x, y] := 1 + return + } +end + + +procedure play() + local win, x, y, evt, mark, marks, board, visited, pt, value + sq := (if match("OS/2", &host) then 30 else 20) + x1 := 10 + y1 := 10 + win := WOpen("label=HB", "size=" || width * sq + 2 * x1 || "," || + height * sq + 2 * y1) + repeat { + visited := make_board() + board := set_up_board(win, visited) + marks := make_board(" ") + repeat { + evt := Event(win) + case type(evt) of { + "string": case map(evt) of { + "q": exit() + "r": break next + } + "integer": { + if evt = &lrelease then { + x := (&x - x1) / sq + 1 + y := (&y - y1) / sq + 1 + if legal_move(x, y, visited) then { + value := board[x, y] + if value ~=== "X" then { + # + # Visited a safe square. + # + if value = 0 then + every pt := spread_zeros(board, x, y) do { + write_to_square(win, pt[1], pt[2], pt[3]) + visited[pt[1], pt[2]] := 1 + } + else write_to_square(win, x, y, value) + } + else { + # + # Stepped on a bone -- game over. + # + every x := 1 to width & y := 1 to height do { + value := board[x, y] + write_to_square(win, x, y, "X" === value) + } + draw_hearts(win) + repeat { + evt := Event(win) + case type(evt) of { + "integer": if evt = &lrelease then break + "string": case map(evt) of { + "q": exit() + "r": break + } + } + } + break + } + } + } + else if evt = (&mrelease | &rrelease) then { + x := (&x - x1) / sq + 1 + y := (&y - y1) / sq + 1 + mark := marks[x, y] := if marks[x, y] == " " then "#" else " " + write_to_square(win, x, y, mark) + } + } + } + } + } +end + + +procedure spread_zeros(board, x, y, doneset) + local xx, yy, v, donekey + /doneset := set() + donekey := x || "," || y + if member(doneset, donekey) then fail + insert(doneset, donekey) + (v := board[x, y]) | fail + suspend [x, y, v] + if v === 0 then { + every xx := x - 1 to x + 1 & yy := y - 1 to y + 1 do + if not(x = xx & y = yy) & board[xx, yy] then + suspend spread_zeros(board, xx, yy, doneset) + } +end + + +procedure write_to_square(win, x, y, s) + WAttrib(win, + "x=" || x1 + (x - 1) * sq + sq / 2 - 2, + "y=" || y1 + (y - 1) * sq + sq / 2 + 4) + return writes(win, s) +end + + +procedure get_options(arg) + local opt + opt := options(arg, "h+w+b+B") + height := \opt["h"] | 9 + width := \opt["w"] | 15 + nbr_bones := \opt["b"] | (height * width - 9) / 5 + print_bone_count := opt["B"] + width <:= 5 + height <:= 5 + nbr_bones >:= height * width * 2 / 3 + return opt +end + + +procedure initialize(arg) + randomize() + get_options(arg) + return +end + + +procedure make_board(init_value) + local board + board := list(width) + every !board := list(height, init_value) + return board +end + + +procedure generate_heart_squares() + suspend [1 | (width + 1) / 2 | width, 1 | (height + 1) / 2 | height] +end + + +procedure set_bones(board, nbr_bones) + local i, j, pt, bone_count + every pt := generate_heart_squares() do board[pt[1], pt[2]] := "$" + board[1, 2] := board[2, 1] := board[2, 2] := "$" + bone_count := 0 + every 1 to nbr_bones do { + # + # Loop to find a spot with a path back to the start. If we don't + # find one after several tries, quit placing bones. + # + (every 1 to 20 do { + i := ?width + j := ?height + if /board[i, j] then { + board[i, j] := "X" + if hearts_reachable(board) then { + bone_count +:= 1 + break + } + else board[i, j] := &null + } + }) | break + } + if \print_bone_count then write(&errout, bone_count, " bones") + return +end + + +procedure calc_neighbors(board) + local i, j, ii, jj, neighbors + every i := 1 to width & j := 1 to height do { + if board[i, j] ~=== "X" then { + neighbors := 0 + every ii := i - 1 to i + 1 & jj := j - 1 to j + 1 do { + if board[ii, jj] === "X" then neighbors +:= 1 + } + board[i, j] := neighbors + } + } + return +end + + +procedure hearts_reachable(board) + local pt + every pt := generate_heart_squares() do { + if not path_to_start(pt[1], pt[2], board) then fail + } + return +end + + +procedure path_to_start(x, y, board, doneset) + local xx, yy, donekey + /doneset := set() + if not(board[x, y] ~=== "X") then fail + if x = 1 & y = 1 then return + donekey := x || "," || y + if member(doneset, donekey) then fail + insert(doneset, donekey) + every xx := x - 1 to x + 1 & yy := y - 1 to y + 1 do { + if x = xx & y == yy then next + if path_to_start(xx, yy, board, doneset) then return + } +end + diff --git a/ipl/gprogs/histo.icn b/ipl/gprogs/histo.icn new file mode 100644 index 0000000..3e94ae9 --- /dev/null +++ b/ipl/gprogs/histo.icn @@ -0,0 +1,99 @@ +############################################################################ +# +# File: histo.icn +# +# Subject: Program to display simple histogram +# +# Author: Ralph E. Griswold +# +# Date: December 21, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays a simple histogram based on numbers provided +# in standard input. +# +# The following options are supported: +# +# -s r horizontal scale factors, default 1.0 +# -w i bar width in pixels, default 5 +# -g i gap between bars, default 1 +# -m minimal; set width to 1, gap to 0. +#- n s name for image file, default "untitled" +# +# Note: If there is too much input, there may not be resources to +# open a window, and even if there is, parts may be off-screen. +# +# The histogram is written to <name>.gif +# +# The window is dismissed by a user q event. +# +############################################################################ +# +# Requires: Graphics +# +############################################################################ +# +# Links: numbers, options, wopen +# +############################################################################ + +link numbers +link options +link wopen + +procedure main(args) + local height, window_height, y, window_width, numbers, opts, scale + local number, gap, bar, name + + opts := options(args, "s.w+g+m") + + scale := \opts["s"] | 1 + bar := \opts["w"] | 5 + gap := \opts["g"] | 1 + if \opts["m"] then { + bar := 1 + gap := 0 + } + name := \opts["n"] | "untitled" + + height := bar + gap + + numbers := [] + + while number := read() do { + number := numeric(number) | stop("*** nonnumeric data") + number <:= 0 # clamp negative number to 0 + put(numbers, number) + } + + if *numbers = 0 then stop("*** no data") + + window_height := *numbers * height + gap + + window_width := integer(scale * (max ! numbers) + 10) + + WOpen("canvas=hidden", "label=Histogram", + "size=" || window_width || "," || window_height) | + stop("*** cannot open window") + + y := 0 + + while FillRectangle(0, y + gap, scale * get(numbers), height - gap) do + y +:= height + + WAttrib("canvas=normal") + + until WQuit() + + WriteImage(name || ".gif") + + WClose() + + return + +end diff --git a/ipl/gprogs/hsvpick.icn b/ipl/gprogs/hsvpick.icn new file mode 100644 index 0000000..7b5b765 --- /dev/null +++ b/ipl/gprogs/hsvpick.icn @@ -0,0 +1,205 @@ +############################################################################ +# +# File: hsvpick.icn +# +# Subject: Program to pick RGB or HSV colors +# +# Author: Gregg M. Townsend +# +# Date: November 14, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# hsvpick is a simple HSV color picker. The three sliders on the +# left control red, green, blue; the sliders on the right control +# hue, saturation, value. The equivalent hexadecimal specification +# is displayed in the center. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, slider, evmux, graphics +# +############################################################################ + +link button +link slider +link evmux +link graphics + +$define BevelWidth 2 +$define WindowMargin 10 + +record valrec(r, g, b, h, s, v) +global sl # the six sliders +global val # the six values [0.0 - 1.0] + +global w, h, m, l # geometry options +global sw # slider width +global colr # selected color + +procedure main(args) + local cwin, x, y, ww, hh + + # create window + Window("size=420,300", args) + m := WindowMargin # size of outside margins + w := w := WAttrib("width") - 2 * m # usable width + h := WAttrib("height") - 2 * m # usable height + l := WAttrib("leading") # leading + sw := 20 # set slider width + + # get mutable color to display the selected color + # use a new binding to avoid disturbing fg/bg of &window. + colr := NewColor(&window) | stop("can't allocate mutable color") + cwin := Clone(&window) + Bg(cwin, colr) + + # draw the area showing the color itself + x := 4 * m + 3 * sw + y := m + ww := w - 6 * sw - 6 * m + hh := h - m - 3 * l + BevelRectangle(x, y, ww, hh, -BevelWidth) + EraseArea(cwin, x+BevelWidth, y+BevelWidth, ww-2*BevelWidth, hh-2*BevelWidth) + + # set up sliders to control the colors + val := valrec(0.75, 0.625, 0.50, 0.0, 0.0, 0.0) # initial values + sl := valrec( + slider(&window, setval, 1, m, m, sw, h, 0.0, val.r, 1.0), + slider(&window, setval, 2, sw + 2 * m, m, sw, h, 0.0, val.g, 1.0), + slider(&window, setval, 3, 2 * sw + 3 * m, m, sw, h, 0.0, val.b, 1.0), + slider(&window, setval, 4, w - m - 3 * sw, m, sw, h, 0.0, val.h, 1.0), + slider(&window, setval, 5, w - 2 * sw, m, sw, h, 0.0, val.s, 1.0), + slider(&window, setval, 6, w + m - sw, m, sw, h, 0.0, val.v, 1.0)) + sethsv() # set hsv from rgb + setcolor() # download the colors + + # set up sensors for quitting + quitsensor(&window) # q or Q + button(&window, "QUIT", argless, exit, m + w / 2 - 30, m + h - 20, 60, 20) + + # enter event loop + evmux(&window) +end + +procedure setval(win, i, v) # set color component i to value v + val[i] := v + if i < 4 then + sethsv() # rgb slider moved; set hsv values + else + setrgb() # hsv slider moved; set rgv values + + setcolor() # set color, update display + return +end + +procedure sethsv() # set hsv from rgb values + # based on Foley et al, 2/e, p.592 + local min, max, d + + min := val.r; min >:= val.g; min >:= val.b # minimum + max := val.r; max <:= val.g; max <:= val.b # maximum + d := max - min # difference + + val.v := max # v is max of all values + if max > 0 then + val.s := d / max + else + val.s := 0 # sat is (max-min)/max + + if val.s > 0 then { + if val.g = max then + val.h := 2 + (val.b - val.r) / d # yellow through cyan + else if val.b = max then + val.h := 4 + (val.r - val.g) / d # cyan through magenta + else if val.g < val.b then + val.h := 6 + (val.g - val.b) / d # magenta through red + else + val.h := (val.g - val.b) / d # red through yellow + } + val.h /:= 6 # scale to 0.0 - 1.0 + + # set sliders to reflect calculated values + slidervalue(sl.h, val.h) + slidervalue(sl.s, val.s) + slidervalue(sl.v, val.v) + return +end + +procedure setrgb() # set rgb from hsv values + # based on Foley et al, 2/e, p.593 + local h, f, i, p, q, t, v + + if val.s = 0.0 then + val.r := val.g := val.b := val.v # achromatic + else { + h := val.h * 6.0 # hue [0.0 - 6.0) + if h >= 6.0 then + h := 0.0 + i := integer(h) + f := h - i + v := val.v + p := val.v * (1.0 - val.s) + q := val.v * (1.0 - f * val.s) + t := val.v * (1.0 - (1.0 - f) * val.s) + case i of { + 0: { val.r := v; val.g := t; val.b := p } # red - yellow + 1: { val.r := q; val.g := v; val.b := p } # yellow - green + 2: { val.r := p; val.g := v; val.b := t } # green - cyan + 3: { val.r := p; val.g := q; val.b := v } # cyan - blue + 4: { val.r := t; val.g := p; val.b := v } # blue - magenta + 5: { val.r := v; val.g := p; val.b := q } # magenta - red + } + } + + # set sliders to reflect calculated values + slidervalue(sl.r, val.r) + slidervalue(sl.g, val.g) + slidervalue(sl.b, val.b) + return +end + +procedure setcolor() # set the color in the color map + local s, x + + # build and display hex color spec, and set color + s := "#" || hexv(val.r) || hexv(val.g) || hexv(val.b) + Color(colr, s) + GotoXY(m + w / 2 - TextWidth(s) / 2, m + h - 2 * l) + WWrites(s) + + # display r, g, b values + x := 4 * m + 3 * sw + GotoXY(x, m + h - 2 * l) + WWrites("r: ", right(integer(65535 * val.r), 5)) + GotoXY(x, m + h - l) + WWrites("g: ", right(integer(65535 * val.g), 5)) + GotoXY(x, m + h) + WWrites("b: ", right(integer(65535 * val.b), 5)) + + # display h, s, v values + x := w - 2 * m - 3 * sw - TextWidth("h: 000") + GotoXY(x, m + h - 2 * l) + WWrites("h: ", right(integer(360 * val.h), 3)) + GotoXY(x, m + h - l) + WWrites("s: ", right(integer(100 * val.s), 3)) + GotoXY(x, m + h) + WWrites("v: ", right(integer(100 * val.v), 3)) + return +end + +procedure hexv(v) # two-hex-digit specification of v + static hextab + initial { + every put((hextab := []), !"0123456789ABCDEF" || !"0123456789ABCDEF") + } + return hextab [integer(255 * v + 1.5)] +end diff --git a/ipl/gprogs/hvc.icn b/ipl/gprogs/hvc.icn new file mode 100644 index 0000000..058d57d --- /dev/null +++ b/ipl/gprogs/hvc.icn @@ -0,0 +1,94 @@ +############################################################################ +# +# File: hvc.icn +# +# Subject: Program to pick colors for Tek HVC space +# +# Author: Gregg M. Townsend +# +# Date: November 14, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# hvc is a simple color picker using HVC space. The three sliders +# control hue, value, and chroma from left to right. +# +############################################################################ +# +# Requires: Version 9 graphics under X11R5 +# +############################################################################ +# +# Links: button, slider, evmux, graphics +# +############################################################################ + +link button +link slider +link evmux +link graphics + +$define BevelWidth 2 +$define WindowMargin 10 + +record hvcrec(h, v, c) +global settings, colr, sl, win +global w, h, m + +procedure main(args) + local opts, cwin, ww, hh + + win := Window("size=300,250", "font=Helvetica,bold,14", args) + w := WAttrib("width") + h := WAttrib("height") + m := WindowMargin + + # a mutable color for displaying the selected color + # use a new binding to avoid disturbing fg/bg of win. + colr := NewColor(win) | stop("can't allocate mutable color") + cwin := Clone(win) + Bg(cwin, colr) + Color(win, colr, "TekHVC:0/0/0") | + stop("can't set HVC colors -- need X11R5") + + ww := w - 3 * (m + 20) - 2 * m + hh := h - 30 - 4 * m + BevelRectangle(win, m, m, ww, hh, -BevelWidth) + EraseArea(cwin, m+BevelWidth, m+BevelWidth, ww-2*BevelWidth, hh-2*BevelWidth) + + # set up sliders to control the colors + settings := hvcrec(0.50, 0.75, 0.25) # initial positions + sl := hvcrec( + slider(win, sethvc, 1, w-3*m-60, m, 20, h-2*m, 0.0, settings.h, 1.0), + slider(win, sethvc, 2, w-2*m-40, m, 20, h-2*m, 0.0, settings.v, 1.0), + slider(win, sethvc, 3, w-m-20, m, 20, h-2*m, 0.0, settings.c, 1.0)) + setcolor() # download the colors + + # set up sensors for quitting + quitsensor(win) # q or Q + button(win, "QUIT", argless, exit, m, h - m - 20, 60, 20) + + # enter event loop + evmux(win) +end + +procedure sethvc(win, i, v) # set color component i to value v + settings[i] := v + setcolor() +end + +procedure setcolor() # set the color in the color map + local hue, value, chroma, s + hue := integer(360 * settings.h + 0.5) + value := integer(100 * settings.v + 0.5) + chroma := integer(100 * settings.c + 0.5) + s := "TekHVC:" || hue || "/" || value || "/" || chroma + Color(win, colr, s) + GotoXY(win, m, h - 20 - 2 * m) + write(win, left(s, 20)) + return +end diff --git a/ipl/gprogs/img.icn b/ipl/gprogs/img.icn new file mode 100644 index 0000000..557c41b --- /dev/null +++ b/ipl/gprogs/img.icn @@ -0,0 +1,358 @@ +############################################################################ +# +# File: img.icn +# +# Subject: Program to create and edit tiny images +# +# Authors: Gregg M. Townsend and Nolan Clayton +# +# Date: April 9, 2004 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# img is a simple editor of Icon image strings and other tiny images. +# An image size of 64 x 64 pixels is around the practical maximum. +# +# usage: img [-cn | -gn] [filename | width [height]] +# +# -c or -g specifies a palette; the default is -c1. +# +# An input file may contain an image string or an image readable by Icon. +# If no filename is given, a new image (default size 16 x 16) is created. +# +# img brings up a window within which: +# +# -- clicking on the color palette sets the color of that mouse button +# -- clicking on the cell grid sets the color of a cell +# -- shift-clicking on the cell grid sets the button color from the cell +# +# -- pressing "W" writes the image string to standard output +# -- pressing "Q" writes the image string and then exits +# -- pressing "Z" clears all cells to the color of the left mouse button +# -- pressing "O" or "L" toggles palette outlining or labeling +# -- pressing "T" sets the left mouse button to '~' the transparent color +# -- pressing "R" changes pixels matching the right button color +# to be the color of the left button +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, imscolor +# +############################################################################ + +# To Do: +# clearer display of transparent button & pixels +# add "save as" function to write GIF (or whatever) file +# use standard utils for row<->image translation + + +link graphics, imscolor + +$define Border 16 # window border + +$define ColorW 12 # width of color indicator +$define ColorH 24 # height of color indicator + +$define LMar 150 # left margin of cell area +$define MaxCell 24 # maximum cell size + + +global rows, imspec # current image +global palette # color palette +global palx, paly, palw, palh # palette display area +global palf # palette display flags +global buttons # button colors + + +# main program + +procedure main(args) + local wwidth, wheight + local hcells, vcells, cellsize, x0, y0 + local black, white + local i, j, x, y, k, e, c + local imgstr, imgtemp, L + + Window(args) + wwidth := WAttrib("width") # window width + wheight := WAttrib("height") # window height + + palette := "c1" + args[1] ? if ="-" then { + palette := tab(0) + get(args) + } + + if *args > 0 & not integer(args[1]) then { # if filename supplied + imgstr := readicon(args[1]) + palette := imspalette(imgstr) + hcells := imswidth(imgstr) # cells horizontally + vcells := imsheight(imgstr) # cells vertically + } + else { + hcells := integer(args[1]) | 16 # cells horizontally + vcells := integer(args[2]) | hcells # cells vertically + c := PaletteKey(palette, "white") + imgstr := hcells || "," || palette || "," || repl(c, vcells * hcells) + } + + cellsize := MaxCell # cell size on window + cellsize >:= wheight / (vcells + 4) + cellsize >:= (wwidth - LMar) / (hcells + 4) + if cellsize < 2 then + stop("image is too large for this window") + + palx := Border + paly := Border + vcells + Border + palw := LMar - 2 * Border + palh := wheight - Border - paly + palf := "u" + drawpalette(palette, palx, paly, palw, palh, palf) + + x0 := wwidth / 2 - (cellsize * hcells) / 2 + LMar / 2 # UL corner of cells + y0 := wheight / 2 - (cellsize * vcells) / 2 + Fg("gray") + every x := x0 to x0 + (hcells - 1) * cellsize by cellsize do + every y := y0 to y0 + (vcells - 1) * cellsize by cellsize do + DrawRectangle(x, y, cellsize, cellsize) + + black := PaletteKey(palette, "black") + white := PaletteKey(palette, "white") + buttons := table() + setbutton(&lpress, black) + setbutton(&mpress, black) + setbutton(&rpress, white) + + imgtemp := imgstr[find(imspalette(imgstr), imgstr) : 0] + imgtemp := imgtemp[find(',', imgtemp) + 1 : 0] + + rows := [] # list of row values + L := "" + + every y := 1 to vcells do { + every x := 1 to hcells do { + k := imgtemp[((y - 1) * hcells) + x] + L ||:= k + Fg(PaletteColor(palette, k)) + FillRectangle(x0 + ((x - 1) * cellsize), + y0 + ((y - 1) * cellsize), cellsize, cellsize) + } + put(rows, L) + L :="" + } + + Fg("gray") + every x := x0 to x0 + (hcells - 1) * cellsize by cellsize do + every y := y0 to y0 + (vcells - 1) * cellsize by cellsize do + DrawRectangle(x, y, cellsize, cellsize) + + newimage() + + repeat case e := Event() of { + + &lpress | &mpress | &rpress | &ldrag | &mdrag | &rdrag: { + + # mouse on palette: set color + if k := pickpalette(palette, &x - palx, &y - paly, palw, palh) then { + case e of { + &lpress | &ldrag: setbutton(&lpress, k) + &mpress | &mdrag: setbutton(&mpress, k) + &rpress | &rdrag: setbutton(&rpress, k) + } + next + } + + # mouse on cell: set color + j := (&x - x0) / cellsize + i := (&y - y0) / cellsize + if j < 0 | j >= hcells | i < 0 | i >= vcells then + next + x := x0 + j * cellsize + 1 + y := y0 + i * cellsize + 1 + + # if shifted, pick color from grid + if &shift then { + k := rows[i + 1, j + 1] + case e of { + &lpress | &ldrag: setbutton(&lpress, k) + &mpress | &mdrag: setbutton(&mpress, k) + &rpress | &rdrag: setbutton(&rpress, k) + } + next + } + + case e of { + &lpress | &ldrag: k := buttons[&lpress] + &mpress | &mdrag: k := buttons[&mpress] + &rpress | &rdrag: k := buttons[&rpress] + } + Fg(PaletteColor(palette, k)) + FillRectangle(x, y, cellsize - 1, cellsize - 1) + rows[i + 1, j + 1] := k + newimage() + } + + !"oOlL": { # O or L: toggle outlining / labeling + e := map(e) + if palf ? find(e) then + palf := string(palf -- e) + else + palf ||:= e + drawpalette(palette, palx, paly, palw, palh, palf) + } + QuitEvents(): { # Q (etc): quit + imswrite(, imspec) + exit() + } + !"wW": { # W: write pattern to stdout + imswrite(, imspec) + } + + !"tT": { # T: set left mouse button transparent + setbutton(&lpress, '~') + } + + !"rR": { # R: replace colors + colorreplace(buttons[&rpress], buttons[&lpress]) + + every y := 1 to vcells do { + every x := 1 to hcells do { + k := rows[y][x] + Fg(PaletteColor(palette, k)) + FillRectangle(x0 + ((x - 1) * cellsize), + y0 + ((y - 1) * cellsize), cellsize, cellsize) + } + } + + Fg("gray") + every x := x0 to x0 + (hcells - 1) * cellsize by cellsize do + every y := y0 to y0 + (vcells - 1) * cellsize by cellsize do + DrawRectangle(x, y, cellsize, cellsize) + } + + + !"zZ": { # Z: clear pattern + + k := buttons[&lpress] + Fg(PaletteColor(palette, k)) + rows := list(vcells, repl(k, hcells)) + + FillRectangle(x0, y0, hcells * cellsize, vcells * cellsize) + Fg("gray") + every x := x0 to x0 + (hcells - 1) * cellsize by cellsize do + every y := y0 to y0 + (vcells - 1) * cellsize by cellsize do + DrawRectangle(x, y, cellsize, cellsize) + + newimage() + + } + } +end + + +# setbutton(event, key) -- set the color of a button + +procedure setbutton(e, k) + local i, x, y + + buttons[e] := k + i := case e of { + &lpress: 2 + &mpress: 1 + &rpress: 0 + } + x := palx + palw - ColorW - i * (ColorW * 3 / 2) + y := (paly - ColorH) / 2 + Fg(PaletteColor(palette, k)) + FillArc(x, y, ColorW, ColorH) + Fg("black") + DrawArc(x, y, ColorW, ColorH) +end + + +# newimage() -- update image (in memory and onscreen) from rows + +procedure newimage() + imspec := rowstoims(palette, rows) + DrawImage(Border, Border, imspec) + return +end + + +# rowstoims(pal, rows) -- convert array of rows into image string + +procedure rowstoims(pal, rows) + local w, s, im + + w := *rows[1] | fail + im := w || "," || pal || "," + every s := !rows do { + if *s ~= w then fail + im ||:= s + } + return im +end + + +# replacecolor(color1, color2) -- replace color1 with color2 + +procedure colorreplace(color1, color2) + local i, j + + every i := 1 to *rows do + while j := find(color1, rows[i]) do + rows[i][j] := color2 + + newimage() + +end + + +# readicon(fname) -- read image, returning image string + +procedure readicon(fname) + local res, f, x + + f := open(fname) | stop("cannot open " || fname) + + res := "" + + while x := read(f) do { + x ? { + if ="#" then + next + + ="\"" + res ||:= tab(0) + } + + if res[-1] == "_" then + res[-1] := "" + else + break + } + close(f) + + # + # Check for reasonably valid image + # + if imsheight(res) then + return res + else { + if f := open(fname, "g", "image=" || fname, "canvas=hidden") then { + res := Capture(f, palette) + close(f) + if imsheight(res) then return res + } + stop("invalid image: " || fname) + } + +end diff --git a/ipl/gprogs/img2grid.icn b/ipl/gprogs/img2grid.icn new file mode 100644 index 0000000..83952fd --- /dev/null +++ b/ipl/gprogs/img2grid.icn @@ -0,0 +1,65 @@ +############################################################################ +# +# File: img2grid.icn +# +# Subject: Program to convert images to grids +# +# Author: Ralph E. Griswold +# +# Date: May 29, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts an image to a grid of cells. +# +# The options supported are: +# +# -s i size of grid cell; default 4 +# -p s save image of grid with file prefix s; default "img2grid" +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: cells, options, wopen +# +############################################################################ + +link cells +link options +link wopen + +procedure main(args) + local x, y, width, height, c, panel, opts, cellsize, prefix + + opts := options(args, "s+p:") + + cellsize := \opts["s"] | 4 + prefix := \opts["p"] | "img2grid" + + WOpen("image=" || args[1], "canvas=hidden") | stop("*** cannot open image") + + width := WAttrib("width") + height := WAttrib("height") + + panel := makepanel(width, height, cellsize) + + WAttrib(panel.window, "canvas=normal") + + every y := 0 to height - 1 do { + x := 0 + every c := Pixel(0, y, width, 1) do { + colorcell(panel, x + 1, y + 1, c) + x +:= 1 + } + } + + WriteImage(panel.window, prefix || ".gif") + +end diff --git a/ipl/gprogs/imgcolrs.icn b/ipl/gprogs/imgcolrs.icn new file mode 100644 index 0000000..90b10b3 --- /dev/null +++ b/ipl/gprogs/imgcolrs.icn @@ -0,0 +1,58 @@ +############################################################################ +# +# File: imgcolrs.icn +# +# Subject: Program to list colors in images +# +# Author: Ralph E. Griswold +# +# Date: January 6, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program analyzes images whose names are given on the command line +# and produces a file with the lists of colors used in each. The entries +# are given in the order of most to least frequent color. The color +# files have the base name of the image file and the extension ".clr". +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, imgcolor, wopen +# +############################################################################ + +link imgcolor +link basename +link wopen + +procedure main(args) + local file, colors, output, name + + every file := !args do { + WOpen("canvas=hidden", "image=" || file) | { + write(&errout, "*** cannot open image file ", file) + next + } + colors := imgcolor() + WClose() + name := basename(file, ".gif") + output := open(name || ".clr", "w") | { + write("*** cannot open ", name, ".clr") + next + } + colors := sort(colors, 4) + while pull(colors) do + write(output, pull(colors)) + close(output) + &window := &null + } + +end diff --git a/ipl/gprogs/imgpaper.icn b/ipl/gprogs/imgpaper.icn new file mode 100644 index 0000000..eaf39c1 --- /dev/null +++ b/ipl/gprogs/imgpaper.icn @@ -0,0 +1,163 @@ +############################################################################ +# +# File: imgpaper.icn +# +# Subject: Program to tile images to form wallpaper +# +# Author: Ralph E. Griswold +# +# Date: July 14, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program tiles images to fill a window. +# +# The supported options are: +# +# -s read image strings from standard input; default, use +# image file names given on command line +# -p read BLPs from standard input; default as for -s +# -w i window width, default 640 +# -h i window height, default 480 +# -g r gamma; default to Icon default +# -m manual mode; wait for event before going to next image +# -a i automatic mode (default); hold pane for i seconds, default 2 +# -l list names of files on standard output +# -i save GIF file of each image +# -n s prefix for image names, default "paper" +# -b fill window with black at end and hold for event +# -v size for video recording, 342x240; overrides other settings +# -M mirror image before tiling +# +# In the case of the -m option for images, if the event is a letter, the +# letter, a colon, and current image name is printed to standard output. +# In case of the -m option for image strings, if the event is a letter, +# the image string is written. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: imsutils, mirror, options, tiler, xio +# +############################################################################ + +link imsutils +link mirror +link options +link tiler +link xio + +procedure main(args) + local opts, w, h, hold, names, name, prefix, images, count, number + local lines, ims, bad, Hold, mir, background, e, gamma, tmp1, tmp2 + local rows, blp + + Hold := Event + + opts := options(args, "w+h+g.ma+lispn:bvM") + w := \opts["w"] | 640 + h := \opts["h"] | 480 + mir := \opts["M"] + if \opts["v"] then { # size for video recording + w := 320 + h := 240 + } + background := opts["b"] + if /opts["m"] then Event := 1 + hold := (\opts["a"] * 1000.0) | 2000 + names := opts["l"] + images := opts["i"] + prefix := \opts["n"] | "paper" + if (gamma := \opts["g"]) & (gamma <= 0.0) then + stop("gamma value must be greater than 0.0") + number := 0 + count := -1 + + WOpen("size=" || w || "," || h, "fillstyle=textured") | + stop("*** cannot open window") + WAttrib("gamma="|| \opts["g"]) + + if \background then Hold() + + if \opts["s"] then { # image strings + while ims := readims() do { + tileims(&window, ims) | { + write(&errout, "*** cannot draw image") + /bad := open("bad.ims", "a") | stop("*** cannot open bad.ims") + write(bad, ims) + } + WFlush() + if \lines then write(number +:= 1) + if Event === 1 then delay(hold) else { + if Event() === !&letters then write(ims) + } + EraseArea() + } + } + else if \opts["p"] then { # BLPs + while blp := read() do { + rows := pat2rows(blp) + ims := *rows[1] || ",g2," + every ims ||:= !rows + tileims(&window, ims) | { + write(&errout, "*** cannot draw image") + /bad := open("bad.ims", "a") | stop("*** cannot open bad.ims") + write(bad, ims) + } + WFlush() + if \lines then write(number +:= 1) + if Event === 1 then delay(hold) else { + e := Event() + write(!&letters === e, ":", blp) + } + EraseArea() + } + } + else { + every name := !args do { + WAttrib("label=" || name) + if \mir then { + tmp1 := WOpen("image=" || name, "canvas=hidden") + tmp2 := mirror(tmp1) + tile(tmp2, &window) + WClose(tmp1) + WClose(tmp2) + } + else tileimg(&window, name) + if \names then write(name) + if \images then WriteImage(prefix || right(count +:= 1, 3, "0") || + ".gif") + if Event === 1 then delay(hold) else { + e := Event() + write(!&letters === e, ":", name) + } + EraseArea() + } + } + + if \background then { # fill with black and hold? + FillRectangle() + Hold() + } + +end +# +# Produce a list of the rows of a pattern + +procedure pat2rows(pattern) + local rlist + + rlist := [] + + every put(rlist, rowbits(pattern)) + + return rlist + +end diff --git a/ipl/gprogs/imgtolst.icn b/ipl/gprogs/imgtolst.icn new file mode 100644 index 0000000..49a4981 --- /dev/null +++ b/ipl/gprogs/imgtolst.icn @@ -0,0 +1,57 @@ +############################################################################ +# +# File: imgtolst.icn +# +# Subject: Program to convert image to list of pixel colors +# +# Author: Ralph E. Griswold +# +# Date: November 22, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts images to lists of pixel values. The +# first line of output gives the dimensions of the image. +# +# The extension of the image file is replaced by .lst in the list +# file. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, wattrib, wopen +# +############################################################################ + +link basename +link wattrib +link wopen + +procedure main(args) + local file, name, output + + every file := !args do { + name := basename(file, ".gif") + output := open(name || ".lst", "w") | { + write(&errout, "*** cannot open ", name, ".lst") + next + } + WOpen("canvas=hidden", "image=" || file) | { + write(&errout, "*** cannot open ", file) + next + } + write(output, "width=", Width(), " height=", Height()) + every write(output, Pixel()) + WClose() + &window := &null + close(output) + } + +end diff --git a/ipl/gprogs/imlreduc.icn b/ipl/gprogs/imlreduc.icn new file mode 100644 index 0000000..1231129 --- /dev/null +++ b/ipl/gprogs/imlreduc.icn @@ -0,0 +1,66 @@ +############################################################################ +# +# File: imlreduc.icn +# +# Subject: Program to reduce bi-level image strings +# +# Author: Ralph E. Griswold +# +# Date: November 21, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reduces bi-level image strings to their lowest equivalent +# form. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: imxform, imscanon +# +############################################################################ + +link imxform +link imscanon + +procedure main() + local ims, imx, sorter1, sorter2 + + sorter1 := set() + sorter2 := set() + + while ims := readims() do { + imx := imstoimx(ims) # combine later + imx := imxreduce(imx) + ims := imxtoims(imx) + insert(sorter1, ims) + } + + every ims := !sorter1 do { + imx := imstoimx(ims) + imx := imxrotate(imx, "cw") + ims := imxtoims(imx) + ims := imscanon(ims) + insert(sorter2, ims) + } + + sorter1 := set() + + every ims := !sorter2 do { + imx := imstoimx(ims) + imx := imxrotate(imx, "ccw") + ims := imxtoims(imx) + ims := imscanon(ims) + insert(sorter1, ims) + } + + every write(!sorter1) + +end diff --git a/ipl/gprogs/imltogif.icn b/ipl/gprogs/imltogif.icn new file mode 100644 index 0000000..4fa2bfe --- /dev/null +++ b/ipl/gprogs/imltogif.icn @@ -0,0 +1,85 @@ +############################################################################ +# +# File: imltogif.icn +# +# Subject: Program to convert image strings to GIF files +# +# Author: Ralph E. Griswold +# +# Date: May 23, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts a list of image strings given in standard input +# to corresponding GIF images. +# +############################################################################ +# +# The options supported are: +# +# -n s sets prefix for image file names to s, default "image" +# -c i number of columns for serial numbers in file names; +# default 4 +# -f i first number, default 1 +# -p treats image string as a pattern and fills a square +# window of its maximum dimension +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: imageseq, imutils, numbers, options, wopen +# +############################################################################ + +link imageseq +link imutils +link numbers +link options +link wopen + +procedure main(args) + local count, ims, image, w, h, s, opts, pattern, prefix + + count := 0 + + opts := options(args, "n:c+f+p") + /opts["c"] := 4 + + seq_init(opts) + pattern := opts["p"] + + while ims := read() do { + count +:= 1 + w := imswidth(ims) + h := imsheight(ims) + if (w | h) = 0 then { + write(&errout, "line ", count, ": bad image string") + next + } + if \pattern then w := h := max(w,h) + image := WOpen("canvas=hidden", "size=" || w || "," || h) | { + write(&errout, "line ", count, ": cannot open window") + next + } + if \pattern then { + WAttrib(image, "fillstyle=opaquepatterned") + Pattern(image, ims) + FillRectangle(image) + } + else DrawImage(image, 0, 0, ims) | { + write(&errout, "line ", count, ": cannot draw image") + WClose(image) + next + } + save_image(image) + WClose(image) + } + +end diff --git a/ipl/gprogs/ims2pat.icn b/ipl/gprogs/ims2pat.icn new file mode 100644 index 0000000..599ae3e --- /dev/null +++ b/ipl/gprogs/ims2pat.icn @@ -0,0 +1,42 @@ +############################################################################ +# +# File: ims2pat.icn +# +# Subject: Program to convert image string to bi-level pattern +# +# Author: Ralph E. Griswold +# +# Date: April 20, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# This program converts an image string with the g2 palette to a +# bi-level pattern. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: imrutils, imsutils, wopen +# +############################################################################ + +link imrutils +link imsutils +link wopen + +procedure main() + local imr + + imr := imstoimr(read()) + + imropen(imr) + + write(pix2pat(&window, 0, 0, WAttrib("width"), WAttrib("height"))) + +end diff --git a/ipl/gprogs/imstogif.icn b/ipl/gprogs/imstogif.icn new file mode 100644 index 0000000..92f9521 --- /dev/null +++ b/ipl/gprogs/imstogif.icn @@ -0,0 +1,66 @@ +############################################################################ +# +# File: imstogif.icn +# +# Subject: Program to convert image strings to GIF files +# +# Author: Ralph E. Griswold +# +# Date: June 23, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts image strings whose names are given on the command +# line to GIF files. Image files are expected to have the suffix .ims. +# +# The GIF files are written to files with the basenames of the image string +# files and the suffix "gif". +# +# The following option is supported: +# +# -l read Icon literal instead of plain string +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, graphics, imrutils, options, strings +# +############################################################################ + +link basename +link graphics +link imrutils +link options +link strings + +procedure main(args) + local file, opts, name, imr, input, literal + + opts := options(args, "l") + + literal := opts["l"] # NOT YET IMPLEMENTED + + every file := !args do { + name := basename(file, ".ims") || ".gif" + input := open(file) | { + write(&errout, "*** can't open ", file) + next + } + imr := imstoimr(read(input)) | { + write(&errout, "*** bad image file: ", file) + next + } + imropen(imr) | stop("*** bad image file: ", file) + close(input) + WriteImage(name) + WClose() + } + +end diff --git a/ipl/gprogs/ipicker.icn b/ipl/gprogs/ipicker.icn new file mode 100644 index 0000000..4ba61ae --- /dev/null +++ b/ipl/gprogs/ipicker.icn @@ -0,0 +1,49 @@ +############################################################################ +# +# File: ipicker.icn +# +# Subject: Program to print name of selected images +# +# Author: Ralph E. Griswold +# +# Date: August 13, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays images listed on the command line and waits for +# user input typed into the wnodw. If the input is the letter "y", +# the name of the image file is written to standard output. If the +# input is "q", the program terminates. Other input is ignored. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(args) + local name + + every name := !args do { + WClose(\&window) + WOpen("image=" || name) | { + write(&errout, "Can't open image ", image(name)) + next + } + case WReads(, 1) of { + "y": write(name) + "q": exit() + } + } + +end diff --git a/ipl/gprogs/isd2disd.icn b/ipl/gprogs/isd2disd.icn new file mode 100644 index 0000000..9a27a89 --- /dev/null +++ b/ipl/gprogs/isd2disd.icn @@ -0,0 +1,41 @@ +############################################################################ +# +# File: isd2disd.icn +# +# Subject: Program to show convert ISD draft to drawdown form +# +# Author: Ralph E. Griswold +# +# Date: November 1, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts an ISD to an ISD with drawdown colors. +# +############################################################################ +# +# Links: weavutil, xcode +# +############################################################################ + +link weavutil +link xcode + +procedure main() + local draft + + isd # fly a kite, linker + + draft := xdecode(&input) + + draft.warp_colors := list(*draft.threading, 1) # black + draft.weft_colors := list(*draft.treadling, 2) # white + draft.color_list := [ColorValue("black"), ColorValue("white")] + + xencode(draft, &output) + +end diff --git a/ipl/gprogs/isd2gif.icn b/ipl/gprogs/isd2gif.icn new file mode 100644 index 0000000..6df330c --- /dev/null +++ b/ipl/gprogs/isd2gif.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: isd2gif.icn +# +# Subject: Program to create woven image from ISD +# +# Author: Ralph E. Griswold +# +# Date: May 23, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads a internal structure draft and creates a GIF image of +# the corresponding weave. The command-line option +# +# -n s +# +# allows the basename for the GIF file to be specified. Otherwise, it +# is take from the name field of the ISD. If other command-line arguments +# are given, they are used as attributes for the window in which the +# woven image is created. +# +############################################################################ +# +# Links: options, weavegif, weavutil, xcode +# +############################################################################ + +link options +link weavegif +link weavutil +link xcode + +procedure main(args) + local draft, width, spacing, bg, opts + + isd # Hands off, linker. + + opts := options(args, "n:") + + width := 5 + spacing := 0 + bg := "black" + + push(args, "canvas=hidden") + + draft := xdecode(&input) | stop("*** cannot decode isd") + + draft.name := \opts["n"] # override if given + + if /draft.name then draft.name := "untitled" + /draft.width := *draft.threading + /draft.height := *draft.treadling + + WriteImage(weavegif(draft, args), + draft.name || ".gif") + +end diff --git a/ipl/gprogs/isd2grid.icn b/ipl/gprogs/isd2grid.icn new file mode 100644 index 0000000..6563622 --- /dev/null +++ b/ipl/gprogs/isd2grid.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: isd2grid.icn +# +# Subject: Program to create grid plots for ISDs +# +# Author: Ralph E. Griswold +# +# Date: May 25, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# NOTE: The drawdown code is patched in from code in pfd2ill.icn and +# uses a different method than the others. One way or another, the +# methods should be made consonant. +# +# The option -n s allows a basename to be specified for the image file. +# It defaults to the name in the ISD. +# +############################################################################ +# +# Requires: Version 9 graphics and large integers +# +############################################################################ +# +# Links: isdplot, options, wopen, xcode +# +############################################################################ +# +# Note: The include file may contain link declarations. +# +############################################################################ + +link isdplot +link options +link wopen +link xcode + +procedure main(args) + local draft, win, opts + + opts := options(args, "n:") + + isd # hands off, linker! + + draft := xdecode(&input) | stop("*** cannot decode draft") + + draft.name := \opts["n"] + + win := plot(draft) | stop("*** plot() failed") + + WAttrib(win, "canvas=normal") + + repeat case Event(win) of { # process low-level user events + !"qQ" : exit() + "s" : WriteImage(win, draft.name || "_d.gif") + } +end diff --git a/ipl/gprogs/isd2ill.icn b/ipl/gprogs/isd2ill.icn new file mode 100644 index 0000000..0bc9532 --- /dev/null +++ b/ipl/gprogs/isd2ill.icn @@ -0,0 +1,321 @@ +############################################################################ +# +# File: isd2ill.icn +# +# Subject: Program to create images from ISDs +# +# Author: Ralph E. Griswold +# +# Date: April 23, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program creates Encapsulated PostScript and GIF image files from +# ISDs. +# +# The following options are supported: +# +# -g draw grid lines on drawdown +# -h hold windows open in visible (-v) mode +# -p add showpage for printing +# -s i cell size, default 6 +# -v show images during creation; default, don't +# +# Other options to be added include the control of layout and orientation. +# +# Names of ISDs are taken from the command line. For each, six Encap- +# PostScript files are created: +# +# <base name>_tieup.eps (if given) +# <base name>_liftplan.eps (if given) +# <base name>_threading.eps +# <base name>_treadling.eps +# <base name>_drawdown.eps +# <base name>_pattern.eps (colored "drawdown") +# +# Corresponding GIFs also are produced. +# +# Future plans call for handling "shaftplans" specifying what diagrams +# are wanted. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, interact, options, psrecord, weavutil, xcode +# +############################################################################ + +link basename +link interact +link options +link psrecord +link weavutil +link xcode + +global canvas +global cellsize +global gridlines +global hold +global name +global printing +global draft + +$define CellSize 6 + +procedure main(args) + local opts, input, file + + isd + + opts := options(args, "ghps+v") + + if /opts["p"] then printing := 1 + if \opts["v"] then { + canvas := "canvas=normal" + hold := opts["h"] # only if images are visible + } + else canvas := "canvas=hidden" + + gridlines := opts["g"] + + cellsize := \opts["s"] | CellSize + + while file := get(args) do { + input := open(file) | { + Notice("Cannot open " || file) + next + } + name := basename(file, ".isd") + draft := xdecode(input) + + draw_panes() + close(input) + } + +end + +procedure clear_pane(win, n, m, size) + local x, y, width, height, save_fg + + width := n * size + 1 + height := m * size + 1 + + save_fg := Fg(win) + + Fg(win, "black") + + every x := 0 to width by size do + DrawLine(win, x, 0, x, height) + + every y := 0 to height by size do + DrawLine(win, 0, y, width, y) + + Fg(win, save_fg) + + return + +end + +procedure draw_panes() + local i, j, x, y, treadle, k, treadle_list, c, color + local tieup_win, threading_win, treadling_win, liftplan_win + local drawdown_win, pattern_win + + if \draft.tieup then { + + tieup_win := WOpen(canvas, "width=" || (cellsize * draft.treadles + 1), + "height=" || (cellsize * draft.shafts + 1)) + + PSStart(tieup_win, name || "_tieup.eps") + + clear_pane(tieup_win, draft.treadles, draft.shafts, cellsize) + + every i := 1 to draft.shafts do + every j := 1 to draft.treadles do { + if draft.tieup[j, i] == "1" then + fillcell(tieup_win, j, i, "black") + } + + PSDone(printing) + + WriteImage(tieup_win, name || "_tieup.gif") + + } + + if *\draft.liftplan > 0 then { + + liftplan_win := WOpen(canvas, "width=" || (cellsize * draft.shafts + 1), + "height=" || (cellsize * *draft.treadling + 1)) + + PSStart(liftplan_win, name || "_liftplan.eps") + + clear_pane(liftplan_win, draft.shafts, *draft.treadling, cellsize) + + every i := 1 to *draft.treadling do + every j := 1 to draft.treadles do { + if draft.liftplan[i, j] == "1" then + fillcell(liftplan_win, j, i, "black") + } + + PSDone(printing) + + WriteImage(liftplan_win, name || "_liftplan.gif") + + } + + threading_win := WOpen(canvas, "width=" || (cellsize * *draft.threading + 1), + "height=" || (cellsize * draft.shafts) + 1) + + PSStart(threading_win, name || "_threading.eps") + + clear_pane(threading_win, *draft.threading, draft.shafts + 1, cellsize) + + every i := 1 to *draft.threading do + fillcell(threading_win, i, draft.threading[i], "black") + + PSDone(printing) + + WriteImage(threading_win, name || "_threading.gif") + + treadling_win := WOpen(canvas, "height=" || (cellsize * *draft.treadling + 1), + "width=" || (cellsize * draft.treadles + 1)) + + PSStart(treadling_win, name || "_treadling.eps") + + clear_pane(treadling_win, draft.treadles + 1, *draft.treadling, cellsize) + every i := 1 to *draft.treadling do + fillcell(treadling_win, draft.treadling[i], i, "black") + + PSDone(printing) + + WriteImage(treadling_win, name || "_treadling.gif") + + pattern_win := WOpen(canvas, "width=" || (cellsize * *draft.threading + 1), + "height=" || (cellsize * *draft.treadling + 1)) + + PSStart(pattern_win, name || "_pattern.eps") + + clear_pane(pattern_win, draft.shafts, draft.treadles, cellsize) + + if *cset(draft.warp_colors) = 1 then { # warp solid black + Fg(pattern_win, draft.color_list[draft.warp_colors[1]]) + FillRectangle(pattern_win, 0, 0, *draft.threading * cellsize, + *draft.treadling * cellsize) + } + else { + every i := 0 to *draft.threading - 1 do { # warp striped + Fg(pattern_win, draft.color_list[draft.warp_colors[i]]) + FillRectangle(pattern_win, i * cellsize, 0, cellsize - 1, + *draft.treadling * cellsize) + } + } + + Fg(pattern_win, "black") + + treadle_list := list(draft.treadles) + every !treadle_list := [] + + every i := 1 to draft.treadles do + every j := 1 to draft.shafts do + if draft.tieup[i, j] == "1" then + every k := 1 to *draft.threading do + if draft.threading[k] == j then + put(treadle_list[i], k, 0) + + every y := 1 to *draft.treadling do { + treadle := draft.treadling[y] + color := draft.color_list[draft.weft_colors[y]] + if *treadle_list[treadle] = 0 then next # blank pick + every i := 1 to *treadle_list[treadle] by 2 do + fillcell(pattern_win, treadle_list[treadle][i], y, color) + } + + Fg(pattern_win, "black") + + if \gridlines then { + every x := 0 to WAttrib(pattern_win, "width") by cellsize do + DrawLine(pattern_win, x, 0, x, WAttrib(pattern_win, "height")) + every y := 0 to WAttrib(pattern_win, "height") by cellsize do + DrawLine(pattern_win, 0, y, WAttrib(pattern_win, "width"), y) + } + + PSDone(printing) + + WriteImage(pattern_win, name || "_pattern.gif") + + drawdown_win := WOpen(canvas, "width=" || (cellsize * *draft.threading + 1), + "height=" || (cellsize * *draft.treadling + 1)) + + PSStart(drawdown_win, name || "_drawdown.eps") + + clear_pane(drawdown_win, draft.shafts, draft.treadles, cellsize) + + Fg(drawdown_win, "white") + + FillRectangle(drawdown_win, 0, 0, *draft.threading * cellsize, + *draft.treadling * cellsize) + + treadle_list := list(draft.treadles) + every !treadle_list := [] + + every i := 1 to draft.treadles do + every j := 1 to draft.shafts do + if draft.tieup[i, j] == "1" then + every k := 1 to *draft.threading do + if draft.threading[k] == j then + put(treadle_list[i], k, 0) + + every y := 1 to *draft.treadling do { + treadle := draft.treadling[y] + if *treadle_list[treadle] = 0 then next # blank pick + every i := 1 to *treadle_list[treadle] by 2 do + fillcell(drawdown_win, treadle_list[treadle][i], y, "black") + } + + Fg(drawdown_win, "black") + + if \gridlines then { + every x := 0 to WAttrib(drawdown_win, "width") by cellsize do + DrawLine(drawdown_win, x, 0, x, WAttrib(drawdown_win, "height")) + every y := 0 to WAttrib(drawdown_win, "height") by cellsize do + DrawLine(drawdown_win, 0, y, WAttrib(drawdown_win, "width"), y) + } + + PSDone(printing) + + WriteImage(drawdown_win, name || "_drawdown.gif") + + if \hold then { + repeat { + if Event(Active()) === "q" then break + } + } + + every WClose(tieup_win | \liftplan_win | threading_win | treadling_win | + pattern_win, drawdown_win) + + return + +end + +procedure fillcell(win, n, m, color) + local save_fg + + save_fg := Fg(win) + Fg(win, color) + + FillRectangle(win, (n - 1) * cellsize, (m - 1) * cellsize, cellsize, + cellsize) + + Fg(win, save_fg) + + return + +end diff --git a/ipl/gprogs/isd2wif.icn b/ipl/gprogs/isd2wif.icn new file mode 100644 index 0000000..874998c --- /dev/null +++ b/ipl/gprogs/isd2wif.icn @@ -0,0 +1,134 @@ +############################################################################ +# +# File: isd2wif.icn +# +# Subject: Program to produce WIF from ISD +# +# Author: Ralph E. Griswold +# +# Date: April 14, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces a WIF from an ISD. +# +############################################################################ +# +# Links: patxform, weavutil, xcode +# +############################################################################ + +link patxform +link weavutil +link xcode + +procedure main() + local draft, i, lift_table, line + + isd # protect from linker + + draft := xdecode(&input) | stop("*** cannot decode ISD") + + write("[WIF]") + write("Version=1.1") + write("Date=" || &dateline) + write("Developers=ralph@cs.arizona.edu") + write("Source Program=seqdraft.icn") + + write("[CONTENTS]") + write("Color Palette=yes") + write("Text=yes") + write("Notes=yes") + write("Weaving=yes") + write("Tieup=yes") + write("Color Table=yes") + write("Threading=yes") + write("Treadling=yes") + write("Warp colors=yes") + write("Weft colors=yes") + write("Warp=yes") + write("Weft=yes") + + write("[COLOR PALETTE]") + write("Entries=", *draft.color_list) + write("Form=RGB") + write("Range=0," || 2 ^ 16 - 1) + + write("[TEXT]") + write("Title=", draft.name) + write("Author=Ralph E. Griswold") + write("Address=5302 E. 4th St., Tucson, AZ 85711-2304") + write("EMail=ralph@cs.arizona.edu") + write("Telephone=520-881-1470") + write("FAX=520-325-3948") + + write("[NOTES]") + write("1=") + + + write("[WEAVING]") + write("Shafts=", draft.shafts) + write("Treadles=", draft.treadles) + write("Rising shed=yes") + + write("[WARP]") + write("Threads=", *draft.threading) + write("Units=Decipoints") + write("Thickness=10") + + write("[WEFT]") + write("Threads=", *draft.treadling) + write("Units=Decipoints") + write("Thickness=10") + + # These are provided to produce better initial configurations when + # WIFs are imported to some weaving programs. + + write("[WARP THICKNESS]") + write("[WEFT THICKNESS]") + + write("[COLOR TABLE]") + + every i := 1 to *draft.color_list do + write(i, "=", ColorValue(draft.color_list[i])) + + write("[THREADING]") + every i := 1 to *draft.threading do + write(i, "=", draft.threading[i]) + + write("[TREADLING]") + every i := 1 to *draft.treadling do + write(i, "=", draft.treadling[i]) + + write("[WARP COLORS]") + every i := 1 to *draft.warp_colors do + write(i, "=", draft.warp_colors[i]) + + write("[WEFT COLORS]") + every i := 1 to *draft.weft_colors do + write(i, "=", draft.weft_colors[i]) + + draft.tieup := protate(draft.tieup) + + write("[TIEUP]") + every i := 1 to *draft.tieup do + write(i, "=", tromp(draft.tieup[i])) + +end + +procedure tromp(treadle) + local result + + result := "" + + treadle ? { + every result ||:= upto("1") || "," + } + + return result[1:-1] + +end diff --git a/ipl/gprogs/isd2xgrid.icn b/ipl/gprogs/isd2xgrid.icn new file mode 100644 index 0000000..01f8cb2 --- /dev/null +++ b/ipl/gprogs/isd2xgrid.icn @@ -0,0 +1,58 @@ +############################################################################ +# +# File: isd2xgrid.icn +# +# Subject: Program to create grid plots for ISDs +# +# Author: Ralph E. Griswold +# +# Date: July 4, 2002 +# +############################################################################ +# +# NOTE: The drawdown code is patched in from code in pfd2ill.icn and +# uses a different method than the others. One way or another, the +# methods should be made consonant. +# +# The option -n s allows a basename to be specified for the image file. +# It defaults to the name in the ISD. +# +# This version is for ISDs without explicit thread-color information. +# +############################################################################ +# +# Requires: Version 9 graphics and large integers +# +############################################################################ +# +# Links: isdxplot, options, wopen, xcode +# +############################################################################ +# +# Note: The include file may contain link declarations. +# +############################################################################ + +link isdxplot +link options +link wopen +link xcode + +procedure main(args) + local draft, win, opts + + opts := options(args, "n:") + + isd # hands off, linker! + + draft := xdecode(&input) | stop("*** cannot decode draft") + + draft.name := \opts["n"] + + &dump := 1 + win := plot(draft, "clip") | stop("*** plot() failed") + &dump := 0 + + WriteImage(win, draft.name || "_d.gif") + +end diff --git a/ipl/gprogs/iview.icn b/ipl/gprogs/iview.icn new file mode 100644 index 0000000..de6ddaa --- /dev/null +++ b/ipl/gprogs/iview.icn @@ -0,0 +1,63 @@ +############################################################################ +# +# File: iview.icn +# +# Subject: Program to display image files +# +# Author: Ralph E. Griswold +# +# Date: January 22, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program is modeled after the Unix xview(1) utility. It takes +# a list of image files on the command line and displays them in +# order. The character "n" typed when the mouse cursor is in the +# image window goes to the next image. The character "q" terminates +# the display. +# +# This program can, of course, only display image types that Icon +# understands. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(args) + local name, posx, posy + + posx := posy := 20 + + every name := !args do { + WOpen("image=" || name, "posx=" || posx, "posy=" || posy) | { + write(&errout, "*** cannot open image: ", name) + next + } + repeat { + case Event() of { + "n": { + posx := WAttrib("posx") + posy := WAttrib("posy") + WClose() + break + } + "q": exit() + } + } + } + +end + + diff --git a/ipl/gprogs/julia1.icn b/ipl/gprogs/julia1.icn new file mode 100644 index 0000000..5ff91d8 --- /dev/null +++ b/ipl/gprogs/julia1.icn @@ -0,0 +1,79 @@ +############################################################################ +# +# File: julia1.icn +# +# Subject: Program to display the Julia set +# +# Author: Ralph E. Griswold +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is a barebones version of a display of the Julia set. It +# has deliberately been left simple and free of options so that the +# basic idea is clear and so that it can be used as the basis of +# more capable versions. +# +# This program is based on material given in "Chaos, Fractals, +# and Dynamics", Robert L. Devaney, Addison-Wesley, 1990. +# +# The point in the complex plane for which the Julia set is computed +# is given on the command line, as in +# +# julia1 .360284 .100376 +# +# which displays the Julia set for the complex number .360284 + .100376i. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(argl) + local c1, c2, extent, half, quarter, m, n, x0, y0, x, y + local x1, y1, i, z + + c1 := real(argl[1]) | -1.0 # default is -1.0 + 0.0i + c2 := real(argl[2]) | 0.0 + + extent := 200 + half := 200 / 2 + quarter := real(extent) / 4 + + WOpen("label=julia", "height=" || extent, "width=" || extent) | + stop("*** cannot open window") + + every m := 0 to extent do { + x0 := -2 + m / quarter + every n := 0 to half do { + y0 := 2 - n / quarter + x := x0 + y := y0 + every i := 1 to 20 do { # compute orbit + x1 := x ^ 2 - y ^ 2 + c1 + y1 := 2 * x * y + c2 + x := x1 + y := y1 + z := x ^ 2 + y ^ 2 + if z > 4 then break next # if escaping, forget it + } + DrawPoint(m, n) + DrawPoint(extent - m, extent - n) + } + } + + Event() + +end diff --git a/ipl/gprogs/kaleid.icn b/ipl/gprogs/kaleid.icn new file mode 100644 index 0000000..11b3ed9 --- /dev/null +++ b/ipl/gprogs/kaleid.icn @@ -0,0 +1,381 @@ +############################################################################ +# +# File: kaleid.icn +# +# Subject: Program to produce kaleidoscope +# +# Author: Stephen B. Wampler +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Lots of options, most easily set by with the interface after +# startup. The only one that isn't set that way is -wn where 'n' is +# the size of the kaleidoscope window (default is 600 square). +# +# Terminology (and options): +# +# Window_size (-wN): How big of a display window to use. +# At the current time, this can only be set via a +# command line argument. +# +# Density (-dN): How many circles per octant to keep on display +# at any one time. There is NO LIMIT to the density. +# +# Duration (-lN): How long to keep drawing circles (measured in +# in circles) once the density is reached. There is NO LIMIT +# to the duration. +# +# MaxRadius (-MN): Maximum radius of any circle. +# +# MinRadius (-mN): Preferred minimum radius. Circles with centers +# near the edge have their radii forced down to fit entirely +# on the display +# +# MaxOffset (-XN): Maximum offset from center of display (may wrap). +# +# MinOffset (-xN): Minimum offset +# +# Skew (-sN): Shift probability of placing a circle at a 'typical' +# offset. +# +# Fill (-F): Turns off filling the circles. +# +# Clear (-C): After the duration, reduces density back to 0 before +# quitting. +# +# Random Seed: (-rN): Sets the random number seed. +# +# Thanks to Jon Lipp for help on using vidgets, and to Mary Camaron +# for her Interface Builder. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: vidgets, vslider, vtext, vbuttons, vradio, wopen, xcompat +# +############################################################################ + +link vidgets +link vslider +link vtext +link vbuttons +link vradio +link wopen +link xcompat + +global Clear, fill, duration, density, maxoff, minoff +global maxradius, minradius, r_seed, skew, win_size, mid_win +global root, check1, mainwin, use_dialog +global draw_circle + +global du_v, de_v, rs_v, sk_v + +procedure main (args) + + draw_circle := DrawCircle + + init_globs() + process_args(args) + + if \use_dialog then { # have vidgets, so use them for args. + mainwin := WOpen("label=Kaleidoscope", "width=404", "height=313", + "font=6x12") | + stop ("bad mainwin") + root := ui (mainwin) + GetEvents (root, quit) + } + else { # just rely on command line arguments + kaleidoscope(r_seed) + } + +end + +procedure init_globs() + + duration := 500 # set default characteristics + density := 30 + win_size := 600 + minoff := 1 + maxradius := 150 + minradius := 1 + skew := 1 + fill := "On" + draw_circle := FillCircle + Clear := "Off" + r_seed := map("HhMmYy", "Hh:Mm:Yy", &clock) + # See if the Vidget library is available or not + if \VSet then use_dialog := "yes" + else use_dialog := &null + +end + +procedure process_args(args) + local arg + + # really only needed if you don't use the dialog box + every arg := !args do case arg[1+:2] of { + "-w" : win_size := integer(arg[3:0]) # window size + "-d" : density := integer(arg[3:0]) # density of circles + "-l" : duration := integer(arg[3:0]) # duration + "-M" : maxradius := integer(arg[3:0]) # maximum radius + "-m" : minradius := integer(arg[3:0]) # minimum radius + "-X" : maxoff := integer(arg[3:0]) # maximum offset + "-x" : minoff := integer(arg[3:0]) # minimum offset + "-s" : skew := numeric(arg[3:0]) # set skewedness + "-F" : fill := &null # turn off fill + "-C" : Clear := "yes" # turn on clear mode + "-r" : r_seed := integer(arg[3:0]) # random seed + "-h" : stop("usage: kal [-wn] [-dn] [-ln] [-Mn] [-mn] [-Xn] [-xn] _ + [-sn] [-F] [-C] [-rn]") + } + # adjust parameters that depend on the window size... + mid_win := win_size/2 + maxoff := win_size-1 +end + +# Lorraine Callahan's kaleidoscope program, translated into icon. +# (some of the things she did were too sophisticated for me +# to spend time to figure out, so the output is square instead of +# round), and I use 'xor' to draw instead of writing to separate +# bit planes. + +global putcircle, clrcircle + +procedure kaleidoscope(r) + local colors + + # What colors to use? This can be changed to whatever! + colors := ["red","green","blue","cyan","magenta","yellow"] + + &window := WOpen("label=Kaleidoscope: 'q' quits", "width="||win_size, + "height="||win_size, "bg=black") + WAttrib("drawop=xor") + + # Create two *indentical* sequences of circles, one to use when + # when drawing, one for erasing. (Since 'xor' is used to + # place them, these both just draw the circles!) + + putcircle := create { # draws sequence of circles + &random :=: r + |{ + Fg(?colors) + outcircle() + &random <-> r + } + } + + clrcircle := create { # erases sequence of circles + &random :=: r + |{ + Fg(?colors) + outcircle() + &random <-> r + } + } + + every 1 to density do @putcircle # fill screen to density + + every 1 to duration do { # maintain steady state + @putcircle + @clrcircle + if *Pending(&window) > 0 then break + } + + every (Clear == "On") & 1 to density do @clrcircle + + close(&window) +end + + +procedure outcircle() # select a circle at random, +local radius, xoff, yoff # draw it in kaleidoscopic form + + # get a random center point and radius + xoff := (?(maxoff - minoff) + minoff) % mid_win + yoff := (?(maxoff - minoff) + minoff) % mid_win + radius := ?0 ^ skew + # force radius to 'fit' + radius := ((maxradius-minradius) * radius + minradius) % + (mid_win - ((xoff < yoff)|xoff)) + + # put into all 8 octants + draw_circle(mid_win+xoff, mid_win+yoff, radius) + draw_circle(mid_win+xoff, mid_win-yoff, radius) + draw_circle(mid_win-xoff, mid_win+yoff, radius) + draw_circle(mid_win-xoff, mid_win-yoff, radius) + + draw_circle(mid_win+yoff, mid_win+xoff, radius) + draw_circle(mid_win+yoff, mid_win-xoff, radius) + draw_circle(mid_win-yoff, mid_win+xoff, radius) + draw_circle(mid_win-yoff, mid_win-xoff, radius) + + return +end + + +############################################################################ +# +# Vidget-based user interface -- developed originally using Mary +# Camaron's XIB program. Don't expect this to be very readable - +# you should have to play with it! +# +############################################################################ +procedure ui (win) + local cv1, cv2, cv3, cv4 + local + radio_button2, + radio_button1, + text_input6, + text_input5, + slider4, + slider3, + text_input4, + text_input3, + slider2, + slider1 + + /win := WOpen("label=ui", "width=404", "height=313", "font=6x12") | + stop ("bad win") + root := Vroot_frame (win) + + VInsert (root, Vmessage(win, win_size/2), 168, 98) + VInsert (root, Vmessage(win, "1"), 108, 97) + + VInsert (root, sk_v := Vtext(win,"Skew:\\=1",get_skew,,6), 280, 39) + + VInsert (root, du_v := Vtext(win, "Duration:\\="||duration, get_duration,,9), + 237, 15) + + VInsert (root, Vmessage(win, "Clear at end?"), 232, 145) + VInsert (root, Vmessage(win, "Fill?"), 105, 142) + VInsert (root, Vmessage(win,"Quit?"), 267, 259) + VInsert (root, Vmessage(win,"Display it?"), 26, 260) + + VInsert (root, Vcheckbox(win, do_quit, "check2",20), 305, 255, 20, 20) + + VInsert (root, check1:=Vcheckbox(win, do_display, "check1",20), + 106, 258, 20, 20) + + radio_button2 := Vradio_buttons (win, ["On", "Off"], get_clear, , V_CIRCLE) + VSet(radio_button2,Clear) + VInsert (root, radio_button2, 253, 165) + + radio_button1 := Vradio_buttons (win, ["On", "Off"], get_fill, , V_CIRCLE) + VSet(radio_button1,fill) + VInsert (root, radio_button1, 99, 165) + + cv1 := Vcoupler() + VAddClient(cv1, get_max_offset) + text_input6 := Vtext (win, "Max Offset:\\="||(win_size-1), cv1, , 3) + VAddClient(cv1, text_input6) + slider4 := Vhoriz_slider (win, cv1, "slider4", 70, 12, 0, + win_size-1, win_size-1, ) + VAddClient(cv1, slider4) + VInsert (root, text_input6, 196, 103) + VInsert (root, slider4, 306, 106) + + cv2 := Vcoupler() + VAddClient(cv2, get_min_offset) + text_input5 := Vtext (win, "Min Offset\\=1", cv2, , 3) + VAddClient(cv2, text_input5) + slider3 := Vhoriz_slider (win, cv2, "slider3", 70, 12, 1, win_size-1, 1, ) + VAddClient(cv2, slider3) + VInsert (root, text_input5, 201, 80) + VInsert (root, slider3, 307, 82) + + cv3 := Vcoupler() + VAddClient(cv3, get_max_radius) + text_input4 := Vtext (win, "Max Radius\\="||(win_size/4), cv3, , 3) + VAddClient(cv3, text_input4) + slider2 := Vhoriz_slider (win, cv3, "slider2", 70, 12, 1, win_size/2, + win_size/4, ) + VAddClient(cv3, slider2) + VInsert (root, text_input4, 10, 104) + VInsert (root, slider2, 110, 108) + + cv4 := Vcoupler() + VAddClient(cv4, get_min_radius) + text_input3 := Vtext (win, "Min Radius\\=1", cv4, , 3) + VAddClient(cv4, text_input3) + slider1 := Vhoriz_slider (win, cv4, "slider1", 70, 12, 1, win_size/2, 1, ) + VAddClient(cv4, slider1) + VInsert (root, text_input3, 10, 81) + VInsert (root, slider1, 110, 84) + + VInsert (root, rs_v := Vtext(win,"Random Seed:\\="||r_seed, get_random,, 11), + 30, 41) + VInsert (root, de_v := Vtext(win,"Density:\\="||density, get_density,,8), + 71, 16) + + VResize (root) + return root +end + +procedure get_skew (wit, value) + skew := value +end + +procedure get_duration (wit, value) + duration := value +end + +procedure do_quit (wit, value) + stop() +end + +procedure do_display (wit, value) + r_seed := numeric(rs_v.data) + duration := integer(du_v.data) + density := integer(de_v.data) + skew := integer(sk_v.data) + kaleidoscope(r_seed) + wit.callback.value := &null + VDraw(check1) +end + +procedure get_clear (wit, value) + Clear := value +end + +procedure get_fill (wit, value) + fill := value + if fill == "Off" then draw_circle := DrawCircle + else draw_circle := FillCircle +end + +procedure get_max_offset (wit, value) + maxoff := value +end + +procedure get_min_offset (wit, value) + minoff := value +end + +procedure get_max_radius (wit, value) + maxradius := value +end + +procedure get_min_radius (wit, value) + minradius := value +end + +procedure get_random (wit, value) + r_seed := integer(value) +end + +procedure get_density (wit, value) + density := integer(value) +end + +procedure quit(e) + if e === "q" then stop ("Exiting Kaleidoscope") +end diff --git a/ipl/gprogs/kaleido.icn b/ipl/gprogs/kaleido.icn new file mode 100644 index 0000000..48f1364 --- /dev/null +++ b/ipl/gprogs/kaleido.icn @@ -0,0 +1,337 @@ +############################################################################ +# +# File: kaleido.icn +# +# Subject: Program to produce kaleidoscopic display +# +# Author: Ralph E. Griswold +# +# Date: February 16, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays kaleidoscopic images. The controls on the +# user interface are relatively intuitive -- trying them will give +# a better idea of what's possible than a prose description here. +# +# This program is based on an earlier one by Steve Wampler, which in +# turn was based on a C program by Lorraine Callahan. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, random, vsetup +# +############################################################################ + +link interact +link random +link vsetup + +# Interface globals + +global vidgets # table of vidgets +global root # root vidget +global pause # pause vidget +global size # size of view area (width & height) +global half # half size of view area +global pane # graphics context for viewing +global colors # list of colors + +# Parameters that can be set from the interface + +global delay # delay between drawing circles +global density # number of circles in steady state +global draw_proc # drawing procedure +global max_off # maximum offset of circle +global min_off # minimum offset of circle +global max_radius # maximum radius of circle +global min_radius # minimum radius of circle +global scale_radius # radius scale factor + +# State information + +global draw_list # list of pending drawing parameters +global reset # nonnull when view area needs resetting + +# Record for circle data + +record circle(off1, off2, radius, color) + +$define DelayFactor 200 +$define DensityMax 100 + +$define SliderMax 10.0 # shared knowledge +$define SliderMin 1.0 + +procedure main() + + init() + + kaleidoscope() + +end + +procedure init() + local s + + randomize() + + vidgets := ui() + + root := vidgets["root"] + size := vidgets["region"].uw + if vidgets["region"].uh ~= size then stop("*** improper interface layout") + + delay := 0.5 + density := DensityMax / 2.0 + max_radius := SliderMax # scaled later + min_radius := SliderMin + scale_radius := (size / 4) / SliderMax + + draw_proc := FillCircle + + colors := [] + s := PaletteChars("c3") -- PaletteGrays("c3") + every put(colors, PaletteColor("c3", !s)) + + pause := vidgets["pause"] + + VSetState(pause, 1) + VSetState(vidgets["density"], (density / DensityMax) * SliderMax) + VSetState(vidgets["delay"], delay) + VSetState(vidgets["min_radius"], min_radius * 2) + VSetState(vidgets["max_radius"], max_radius / 2) + VSetState(vidgets["shape"], "discs") + +# Get graphics context for drawing. + + half := size / 2 + + pane := Clone("bg=black", "dx=" || (vidgets["region"].ux + half), + "dy=" || (vidgets["region"].uy + half), "drawop=reverse") + Clip(pane, -half, -half, size, size) + + return + +end + +procedure kaleidoscope() + + # Each time through this loop, the display is cleared and a + # new drawing is started. + + repeat { + + EraseArea(pane, -half, -half, size, size) # clear display + draw_list := [] # new drawing list + reset := &null + + # In this loop a new circle is drawn and an old one erased, once the + # specified density has been reached. This maintains a steady state. + + repeat { + while (*Pending() > 0) | \VGetState(pause) do { + ProcessEvent(root, , shortcuts) + if \reset then break break next + } + putcircle() + WDelay(delay) + + # Don't start clearing circles until the specified density has + # reached. (The drawing list has four elements for each circle.) + + if *draw_list > density then clrcircle() + } + } + +end + +procedure putcircle() + local off1, off2, radius, color + + # get a random center point and radius + + off1 := ?size % half + off2 := ?size % half + radius := ((max_radius - min_radius) * ?0 + min_radius) * scale_radius + radius <:= 1 # don't let them vanish + + color := ?colors + + put(draw_list, circle(off1, off2, radius, color)) + + outcircle(off1, off2, radius, color) + + return + +end + +procedure clrcircle() + local circle + + circle := get(draw_list) + + outcircle( + circle.off1, + circle.off2, + circle.radius, + circle.color + ) + + return + +end + +procedure outcircle(off1, off2, radius, color) + + Fg(pane, color) + + # Draw in symmetric positions. + + draw_proc(pane, off1, off2, radius) + draw_proc(pane, off1, -off2, radius) + draw_proc(pane, -off1, off2, radius) + draw_proc(pane, -off1,-off2, radius) + draw_proc(pane, off2, off1, radius) + draw_proc(pane, off2, -off1, radius) + draw_proc(pane, -off2, off1, radius) + draw_proc(pane, -off2,-off1, radius) + + return + +end + +procedure density_cb(vidget, value) + + density := (value / SliderMax) * DensityMax + density <:= 1 + + reset := 1 + +end + +procedure delay_cb(vidget, value) + + delay := value * DelayFactor + + return + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "snapshot @S": snapshot(pane, -half, -half, size, size) + "quit @Q": exit() + } + + return + +end + +procedure max_radius_cb(vidget, value) + + max_radius := value + + if max_radius < min_radius then { # if max < min lower min + min_radius := max_radius + VSetState(vidgets["min_radius"], min_radius) + } + + reset := 1 + + return + +end + +procedure min_radius_cb(vidget, value) + + min_radius := value + + if min_radius > max_radius then { # if min > max raise max + max_radius := min_radius + VSetState(vidgets["max_radius"], max_radius) + } + + reset := 1 + + return + +end + +procedure reset_cb(vidget, value) + + reset := 1 + + return + +end + +procedure shape_cb(vidget, value) + + draw_proc := case value of { + "discs": FillCircle + "rings": DrawCircle + } + + reset := 1 + + return + +end + +procedure shortcuts(e) + + if &meta then + case map(e) of { # fold case + "q": exit() + "s": snapshot(pane, -half, -half, size, size) + } + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=600,455", "bg=pale gray", "label=kaleido"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,600,455:kaleido",], + ["delay:Slider:h:1:42,120,100,15:1.0,0.0,0.5",delay_cb], + ["density:Slider:h:1:42,180,100,15:0.0,10.0,10.0",density_cb], + ["file:Menu:pull::3,1,36,21:File",file_cb, + ["snapshot @S","quit @Q"]], + ["label01:Label:::13,180,21,13:min",], + ["label02:Label:::152,180,21,13:max",], + ["label03:Label:::13,240,21,13:min",], + ["label04:Label:::152,240,21,13:max",], + ["label05:Label:::13,300,21,13:min",], + ["label06:Label:::152,300,21,13:max",], + ["label07:Label:::7,120,28,13:slow",], + ["label08:Label:::151,120,28,13:fast",], + ["lbl_density:Label:::67,160,49,13:density",], + ["lbl_max_radius:Label:::43,280,98,13:maximum radius",], + ["lbl_min_radius:Label:::44,220,98,13:minimum radius",], + ["lbl_speed:Label:::74,100,35,13:speed",], + ["line:Line:::0,22,600,22:",], + ["max_radius:Slider:h:1:42,300,100,15:0.0,10.0,10.0",max_radius_cb], + ["min_radius:Slider:h:1:42,240,100,15:0.0,10.0,1.0",min_radius_cb], + ["pause:Button:regular:1:33,55,45,20:pause",], + ["reset:Button:regular::111,55,45,20:reset",reset_cb], + ["shape:Choice::2:66,359,64,42:",shape_cb, + ["discs","rings"]], + ["region:Rect:raised::187,40,400,400:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/keypunch.icn b/ipl/gprogs/keypunch.icn new file mode 100644 index 0000000..8eca136 --- /dev/null +++ b/ipl/gprogs/keypunch.icn @@ -0,0 +1,166 @@ +############################################################################ +# +# File: keypunch.icn +# +# Subject: Program to simulate a keypunch +# +# Author: Gregg M. Townsend +# +# Date: February 7, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# In the olden days, computer data was encoded by rectangular holes +# punched in thin pieces of cardboard about the size of an old dollar. +# This program simulates a "keypunch", a mechanical device for punching +# those holes. (Keypunches themselves were programmable, but there's +# no way to program this one; tab stops are set permanently.) +# +# A carriage return feeds a new card. Illegal characters punch a +# lace column. As with a real keypunch, you can backspace, but the +# holes don't go away. +# +# The shift key turns "UIOJKLM<>" into "123456789". The meta key +# serves (imperfectly) as the multipunch key. +# +# The font was chosen on a Sun workstation and may not be portable. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, graphics +# +############################################################################ + + +link options +link optwindw +link graphics + + +global hsiz, vsiz, hsep, vsep, tsep, bsep, lsep, rsep + +procedure main(args) + local win, col, card, c, s, opts + + opts := options(args, winoptions()) + + s := "" + while s ||:= get(args) || " " + + hsiz := 5 + vsiz := 12 + hsep := 3 + vsep := 12 + tsep := 20 + bsep := 20 + lsep := 20 + rsep := 20 + + /opts["B"] := "pale moderate reddish yellow" + /opts["W"] := lsep + 80 * hsiz + 79 * hsep + rsep + /opts["H"] := tsep + 12 * vsiz + 11 * vsep + bsep + win := optwindow(opts) + card := WOpen("canvas=hidden", "width="||opts["W"], "height="||opts["H"]) + + Font(win, "-misc-fixed-medium-r-semi*--13-120-*") + initcard(win) + CopyArea(win, card) + + col := 1 + every c := !map(s, &lcase, &ucase) | keyevent(win) do { + if upto('\^c\^d\d', c) then + exit() + else if upto('\n\r\^u', c) then { + CopyArea(card, win) + col := 1 + } + else if c == '\b' then { + if (col -:= 1) < 1 then + col := 1 + key(win, col, " ") + } + else if c == '\t' then { + col := col + 10 - (col - 1) % 10 + if col > 80 then + col := 80 + } + else { + key(win, col, map(c, &lcase, &ucase)) + if ((not &meta) & (col +:= 1)) > 80 then + col := 80 + } + GotoXY(win, lsep + col * (hsiz + hsep), tsep / 2) + } +end + + +procedure keyevent(win) + local e + repeat { + e := Event(win) + if type(e) == "string" then { + if &shift | &meta then + suspend map(e, "uiojklm,.UIOJKLM<>", "123456789123456789") + else + suspend map(e, &lcase, &ucase) + } + } +end + + +procedure initcard(win) + local i, c + + EraseArea(win) + GotoXY(win, lsep, tsep / 2) + every i := 12 to 3 by -1 do { + c := " 0123456789"[i] + every punch(win, 1 to 80, i, c) + } +end + + +procedure key(win, col, ch) + Fg(win, "black") + every punch(win, col, holes(ch)) + punch(win, col, 0, ch) +end + + +procedure punch(win, col, row, ch) + local x, y, w, h + x := lsep + (col - 1) * (hsiz + hsep) + if row = 0 then + y := 0 + else + y := tsep + (row - 1) * (vsiz + vsep) + if \ch then + DrawString(win, x, y + vsiz - 3, ch) + else + FillRectangle(win, x, y, hsiz, vsiz) +end + + +# Hole codes from CDC SCOPE 3.4 SPRM, Rev. A, 10-15-71, page A-4 (026 encoding). + +procedure holes(c) + static s0, s1, s2, s3, n + initial { + s0 := " 0123456789+ABCDEFGHI-JKLMNOPQR/STUVWXYZ:=@%'[.)^;]$*@?>!,(_#&\"\\" + s1 := " AAAAAAAAAABBBBBBBBBB000000000 AAAAABBBBBB000000 A" + s2 := " 0123456789 123456789 123456789123456789235672346723456723456745" + s3 := " 888888888888888888888888" + } + if n := find(c, s0) then + suspend find((s1 | s2 | s3)[n], "AB0123456789") + else + suspend 1 to 12 +end diff --git a/ipl/gprogs/koch.icn b/ipl/gprogs/koch.icn new file mode 100644 index 0000000..535073f --- /dev/null +++ b/ipl/gprogs/koch.icn @@ -0,0 +1,87 @@ +############################################################################ +# +# File: koch.icn +# +# Subject: Program to demonstrate Koch curves +# +# Author: Stephen B. Wampler +# +# Date: October 14, 1992 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.0 +# +############################################################################ +# +# +# Comments: This program shows how Koch curves work. +# +# See the procedure 'helpmsg' for command line options +# +# Waits for a window event before closing window +# +############################################################################ +# +# Links: glib, wopen +# +############################################################################ +# +# Requires: Version 9 graphics and co-expressions (for glib.icn) +# +############################################################################ + +link glib +link wopen + +global win, mono, h, w +global Window, XMAX, YMAX + +procedure main (args) + local arg, nextarg + + XMAX := YMAX := 700 # physical screen size + w := h := 1.0 + + nextarg := create !args + while arg := @nextarg do { + if arg == ("-help"|"-h") then stop(helpmsg()) + } + + win := WOpen("label=Koch Snowflake", "width="||XMAX, "height="||YMAX) + mono := WAttrib (win, "depth") == "1" + Window := set_window(win, point(0,0), point(w,h), + viewport(point(0,0), point(XMAX, YMAX), win)) + + EraseArea(win) + + Fg(win, "black") + +# koch_line(Window, point(0.25,0.25), point(0.75,0.25), 5) +# koch_line(Window, point(0.75,0.25), point(0.50,0.67), 5) +# koch_line(Window, point(0.50,0.67), point(0.25,0.25), 5) + + koch_line(Window, point(0.00,0.67), point(0.50,0.67), 5) + koch_line(Window, point(0.50,0.67), point(0.25,0.25), 5) + koch_line(Window, point(0.25,0.25), point(0.00,0.67), 5) + + koch_line(Window, point(0.25,0.25), point(0.50,0.67), 5) + koch_line(Window, point(0.50,0.67), point(0.75,0.25), 5) + koch_line(Window, point(0.75,0.25), point(0.25,0.25), 5) + + koch_line(Window, point(0.50,0.67), point(1.00,0.67), 5) + koch_line(Window, point(1.00,0.67), point(0.75,0.25), 5) + koch_line(Window, point(0.75,0.25), point(0.50,0.67), 5) + + Event(win) + close(win) +end + +procedure helpmsg() + write("Usage: Koch") + return +end diff --git a/ipl/gprogs/lindcomp.icn b/ipl/gprogs/lindcomp.icn new file mode 100644 index 0000000..67891f9 --- /dev/null +++ b/ipl/gprogs/lindcomp.icn @@ -0,0 +1,117 @@ +############################################################################ +# +# File: lindcomp.icn +# +# Subject: Program to compile 0L-systems +# +# Author: Ralph E. Griswold +# +# Date: August 13, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts a 0L-system to an Icon program, which when +# executed, produces the corresponding drawing. +# +############################################################################ +# +# See also: linden.icn +# +############################################################################ + +global procs + +procedure main() + local line, sym, new, keyword, value, axiom, gener, angle, length + local replace + + procs := table() # table of procedures to generate + + gener := 4 # defaults + length := 5 + angle := 90.0 + + while line := read() do + line ? { + if sym := tab(find("->")) then { + move(2) + replace := tab(0) + procs[sym] := replace + } + else if keyword := tab(find(":")) then { + move(1) + value := tab(0) + case keyword of { + "axiom": axiom := value + "gener": gener := integer(value) | + stop("*** invalid generation specification") + "angle": angle := real(value) | + stop("*** invalid angle: ", line) + "length": length := integer(value) | + stop("*** invalid length: ", line) + "name": &null # ignore name + default: stop("*** invalid keyword: ", line) + } + } + else stop("*** invalid specification: ", line) + } + + # Write heading and main procedure + + write("link turtle") + write() + write("$define Generations ", gener) + write("$define Angle ", angle) + write("$define Length ", length) + write() + write("procedure main()") + gencode(axiom, "Generations") + write("end") + write() + + # Produce drawing procedures. + + every sym := key(procs) do + genproc(sym, procs[sym]) + +end + +procedure gencode(replace, arg) + local sym + + every sym := !replace do { + case sym of { + "+": write(" TRight(Angle) # +") + "-": write(" TLeft(Angle) # -") + "[": write(" TSave() # [") + "]": write(" TRestore() # ]") + default: if \procs[sym] + then write(" ", sym, "(", arg, ") # ", sym) + } + } + + return + +end + +procedure genproc(name, replace) + + write("procedure ", name, "(gener)") + write(" if gener > 0 then {") + gencode(replace, "gener - 1") + write(" }") + case name of { + "F": write(" else TDraw(Length) # F") + "f": write(" else TSkip(Length) # f") + } + write(" return") + write("end") + write() + + return + +end diff --git a/ipl/gprogs/linden.icn b/ipl/gprogs/linden.icn new file mode 100644 index 0000000..c805949 --- /dev/null +++ b/ipl/gprogs/linden.icn @@ -0,0 +1,213 @@ +############################################################################ +# +# File: linden.icn +# +# Subject: Program to generate sentences in 0L-systems +# +# Author: Ralph E. Griswold +# +# Date: June 21, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads in a 0L-system (Lindenmayer system) consisting of +# rewriting rules in which a string is rewritten with every character +# replaced simultaneously (conceptually) by a specified string of +# symbols. +# +# Rules have the form +# +# S->SSS... +# +# where S is a character. +# +# In addition to rules, there are keywords that describe the system and how +# to draw it. These include the "axiom" on which rewriting is started and +# optionally the angle in degrees between successive lines (default 90). +# Other keywords may be present, but are ignored. +# +# Keywords are followed by a colon. +# +# An example 0L-system is: +# +# X->-FX++FY- +# Y->+FX--FY+ +# F-> +# -->- +# +->+ +# axiom:FX +# angle:45.0 +# xorg:100 +# yorg:100 +# +# Here, the initial string is "FX" and angular increment is 45 degrees. +# Note that "-" is a legal character in a 0L-system -- context determines +# whether it's 0L character or part of the "->" that stands for "is +# replaced by". +# +# If no rule is provided for a character, the character is not changed +# by rewriting. Thus, the example above can be expressed more concisely +# as +# +# X->-FX++FY- +# Y->+FX--FY+ +# F-> +# axiom:FX +# angle:45.0 +# +# The recognized keywords are: +# +# axiom axiom for generation +# angle angular increment for turns +# length segment length +# xorg x origin +# yorg y origin +# comment comment; ignored +# +# Distances increase from left to right in the x direction and from top +# to bottom in the y direction. +# +# As pure-production systems, the characters are symbolic and have no +# meaning. When interpreted for drawing, the characters have the +# following meaning: +# +# F move forward by length +# f move backward by length +# + turn right by angle +# - turn left by angle +# [ save current state +# ] restore current state +# +# The file containing the 0L-systems is read from standard input. +# +# The command-line options are: +# +# -g i number of generations, default 3 +# -l i length of line segments, default 5 +# -a i angular increment in degrees (overrides angle given in +# the grammar) +# -w i window width +# -h i window height +# -x i initial x position, default mid-window +# -y i initial y position, default mid-window +# -W write out string instead of drawing +# -s take snapshot of image. +# -d i delay in milliseconds between symbol interpretations; +# default 0 +# +# NOTE: The name option that supported multiple L-Systems in +# one file has been eliminated on the grounds that it +# introduced too much complexity in use. +# +############################################################################ +# +# References: +# +# Formal Languages, Arto Salomaa, Academic Press, 1973. pp. 234-252. +# +# The Algorithmic Beauty of Plants, Przemyslaw Prusinkiewicz and +# Aristid Lindenmayer, Springer Verlag, 1990. +# +# Lindenmayer Systems, Fractals, and Plants, Przemyslaw Prusinkiewicz and +# James Hanan, Springer Verlag, 1989. +# +############################################################################ +# +# See linden.dat for an example of input data. +# +############################################################################ +# +# Requires: graphics if drawing +# +############################################################################ +# +# Links: linddraw, options, wopen +# +############################################################################ + +link linddraw +link options +link wopen + +procedure main(args) + local line, gener, axiom, angle, opts, i, s, c, symbol, rewrite + local allchars, rhs, value, spec, x, y, length, w, h, delay, name + + rewrite := table() + allchars := '' # cset of all rhs characters + + opts := options(args,"g+l+a+w+h+x+y+Wsd+") + + rhs := '' + + while line := read() do + line ? { + if symbol := move(1) & ="->" then { + rhs := tab(0) + rewrite[symbol] := rhs + allchars ++:= rhs # keep track of all characters + } + else if spec := tab(upto(':')) then { + move(1) + value := tab(0) + case spec of { + "axiom": { + axiom := value + allchars ++:= rhs # axiom might have strays + } + "angle": angle := value + "xorg": x := value + "yorg": y := value + "comment": &null # ignore comments + "length": length := value + "gener": gener := value + "name": name := value + } # ignore others + } + else write(&errout, "malformed input: ", tab(0)) + } + +# At this point, we have the table to map characters, but it may lack +# mappings for characters that "go into themselves" by default. For +# efficiency in rewriting, these mappings are added. + + every c := !allchars do + /rewrite[c] := c + + h := \opts["h"] | 400 + w := \opts["w"] | 400 + + angle := \opts["a"] # command-line overrides + length := \opts["l"] + gener := \opts["g"] + x := \opts["x"] + y := \opts["y"] + delay := \opts["d"] + + /angle := 90 # defaults + /length := 5 + /gener := 3 + /x := 0 + /y := 0 + /delay := 0 + /name := "intitled" + + if /axiom then stop("*** no axiom") + + if /opts["W"] then { + WOpen("size=" || w || "," || h, "dx=" || (w / 2), + "dy=" || (h / 2)) | stop("*** cannot open window") + linddraw(x, y, axiom, rewrite, length, angle, gener, delay) + if \opts["s"] then WriteImage(name || ".gif") + WDone() + } + else { + every writes(lindgen(!axiom, rewrite, gener)) + write() + } + +end diff --git a/ipl/gprogs/lorenz.icn b/ipl/gprogs/lorenz.icn new file mode 100644 index 0000000..d52c239 --- /dev/null +++ b/ipl/gprogs/lorenz.icn @@ -0,0 +1,118 @@ +############################################################################ +# +# File: lorenz.icn +# +# Subject: Program to display Lorenz strange attractor +# +# Author: Ralph E. Griswold +# +# Date: December 5, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is a barebones version of a display of the Lorenz strange +# attractor. It has deliberately been left simple and free of options so +# that the basic idea is clear and so that it can be used as the basis of +# more capable versions. +# +# This program is based on material given in "Fractal, Programming in +# Turbo Pascal", Roger T. Stevens, M&T Books, 1990. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: numbers, wopen +# +############################################################################ + +link numbers +link wopen + +procedure main() + local col, color, colorlist, cx, cy, cz, d0_x, d0_y, d0_z, d1_x + local d1_y, d1_z, d2_x, d2_y, d2_z, d3_x, d3_y, d3_z, dt, dt2 + local i, old_col, old_row, old_y, row, sx, sy, sz, x, x_angle + local xt, y, y_angle, yt, z, z_angle, zt + + x_angle := rtod(45) + sx := sin(x_angle) + cx := cos(x_angle) + y_angle := rtod(0) + sy := sin(y_angle) + cy := cos(y_angle) + z_angle := rtod(90) + sz := sin(z_angle) + cz := cos(z_angle) + + WOpen("label=Lorenz", "width=640", "height=350", + "fg=white", "bg=black") | stop("*** cannot open window") + + colorlist := ["red", "blue", "green", "magenta", "cyan", "yellow"] + + color := colorlist[1] + + x := 0.0 + y := 1.0 + z := 0.0 + old_col := round(y * 9 + 320) + old_row := round(350 - 6.56 * z) + dt := 0.01 + dt2 := dt / 2 + every i := 0 to 8000 do { + d0_x := 10 * (y-x) * dt2 + d0_y := (-x * z + 28 * x - y) * dt2 + d0_z := (x * y - 8 * z / 3) * dt2 + xt := x + d0_x + yt := y + d0_y + zt := z + d0_z + d1_x := (10 * (yt-xt)) * dt2 + d1_y := (-xt * zt + 28 * xt - yt) * dt2 + d1_z := (xt * yt - 8 * zt / 3) * dt2 + xt := x + d1_x + yt := y + d1_y + zt := z + d1_z + d2_x := (10 * (yt-xt)) * dt + d2_y := (-xt * zt + 28 * xt - yt) * dt + d2_z := (xt * yt - 8 * zt / 3) * dt + xt := x + d2_x + yt := y + d2_y + zt := z + d2_z + d3_x := (10 * (yt - xt)) * dt2 + d3_y := (-xt * zt + 28 * xt - yt) * dt2 + d3_z := (xt * yt - 8 * zt / 3) * dt2 + old_y := y + x := x + (d0_x + d1_x + d1_x + d2_x + d3_x) * 0.333333333 + y := y + (d0_y + d1_y + d1_y + d2_y + d3_y) * 0.333333333 + z := z + (d0_z + d1_z + d1_z + d2_z + d3_z) * 0.333333333 + + col := round(y * 9 + 320) + row := round(350 - 6.56 * z) + + if col < 320 then + if old_col >= 320 then { + color := get(colorlist) + put(colorlist, color) + } + else if col > 320 then + if old_col <= 320 then { + color := get(colorlist) + put(colorlist, color) + } + + Fg(color) + DrawLine(old_col, old_row, col, row) + old_row := row + old_col := col + + } + + Event() + +end diff --git a/ipl/gprogs/lsys.icn b/ipl/gprogs/lsys.icn new file mode 100644 index 0000000..3f18c3a --- /dev/null +++ b/ipl/gprogs/lsys.icn @@ -0,0 +1,151 @@ +############################################################################ +# +# File: lsys.icn +# +# Subject: Program to experiment with Lindenmayer systems +# +# Author: Stephen B. Wampler +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.0 +# +############################################################################ +# +# +# Comments: This program display Lindenmayer systems using +# turtle graphics. There are some built-in L-systems, +# but users can easily modify these and construct new +# systems. +# +# See the procedure 'helpmsg' for command line options +# (or run as 'lsys -help') +# +# Waits for a window event before closing window +# +############################################################################ +# +# Requires: Version 9 graphics and co-expressions (for glib.icn) +# +############################################################################ +# +# Links: glib, lsystem, wopen +# +############################################################################ + + +link glib # need the turtle graphics stuff +link lsystem # ...and the L-System stuff +link wopen + +global win, mono, h, w +global Window, XMAX, YMAX + +global pre_defs + +procedure main (args) + local ls, arg, t + + XMAX := YMAX := 700 # physical screen size + w := h := 700.0 + + + init_pre_defs() # table of predefined L-systems + + ls := pre_defs["koch_island"] + + while arg := get(args) do { + case arg of { + "-help"|"-h" : helpmsg() + "-order"|"-o": ls.order := integer(get(args)) + "-dist" |"-d": ls.dist := numeric(get(args)) + "-delta" : ls.delta := numeric(get(args)) + "-axiom"|"-a": ls.axiom := get(args) + "-map" : ls.rewrite[get(args)] := get(args) + "-file"|"-f" : ls := read_Lsystem(open(get(args))) + "-name"|"-n" : ls := \pre_defs[get(args)] + "-describe" : { + write_Lsystem(ls := \(pre_defs[write(get(args))])) + write() + } + } + if arg == ("-help"|"-h") then stop(helpmsg()) + } + + win := WOpen("label=L-System", "width="||XMAX, "height="||YMAX) + mono := WAttrib (win, "depth") == "1" + Window := set_window(win, point(0,0), point(w,h), + viewport(point(0,0), point(XMAX, YMAX), win)) + + EraseArea(win) + + t := turtle(Window, point(w/2, h/2), 0, create |"red") + + eval_lsys(t,ls) +# These two commands are behaviorally equivalent to the above line, +# but trade numerous recursive calls (above) for a *long* command +# string... +# s := build_cmd(ls) +# eval_cmd(t, s, ls.dist, ls.delta) + + # sit and wait for an event on the window. + Event(win) + close(win) +end + +procedure helpmsg() + write("Usage: Lsys [[-o n] [-d r] [-delta r] [-axiom s] [-map c s]... ]") + write(" [-f file] [-n name] [-describe name]") + write(" where") + write(" -o n -- order of system") + write(" -d r -- line length") + write(" -delta r -- angle for turns") + write(" -axiom s -- initial axiom") + write(" -map c S -- rewrite rule mapping c into s") + write(" -f file -- read Lsystem from file") + write(" -n name -- use predefined Lsystem 'name'") + write(" -describe name -- describe (and use) predefined Lsystem 'name'") + write(" ") + write(" Options are processed in order from left to right, e.g.") + write(" ") + write(" Lsys -n koch_island -o 3") + write(" ") + write(" displays an order 3 koch_island.") + write(" ") + write(" Available predefined Lsystems are:\n") + every write(" ",key(pre_defs)) + stop() +end + +procedure init_pre_defs() + + pre_defs := table() + + pre_defs["koch_island"] := Lsys(1,10,90,"F-F-F-F", + mk_map([["F","F-F+F+FF-F-F+F"]])) + pre_defs["box_swirls"] := Lsys(1,10,90,"F-F-F-F", + mk_map([["F","FF-F-F-F-F-F+F"]])) + pre_defs["squares"] := Lsys(1,10,90,"F-F-F-F", + mk_map([["F","FF-F-F-F-FF"]])) + pre_defs["soot"] := Lsys(1,10,90,"F-F-F-F", + mk_map([["F","FF-F--F-F"]])) + pre_defs["box_flake"] := Lsys(1,10,90,"F-F-F-F", + mk_map([["F","F-FF--F-F"]])) + pre_defs["dragon"] := Lsys(1,10,90,"L", + mk_map([["L","L+R+"],["R","-L-R"]])) + pre_defs["triangle"] := Lsys(1,10,60,"R", + mk_map([["L","R+L+R"],["R","L-R-L"]])) + pre_defs["flake"] := Lsys(1,10,60,"L", + mk_map([["L","L+R++R-L--LL-R+"], + ["R","-L+RR++R+L--L-R"]])) + pre_defs["near_hilbert"] := Lsys(1,10,90,"-R", + mk_map([["L","LL-R-R+L+L-R-RL+R+LLR-L+R+LL+R-LR-R-L+L+RR-"], + ["R","+LL-R-R+L+LR+L-RR-L-R+LRR-L-RL+L+R-R-L+L+RR"]])) + +end diff --git a/ipl/gprogs/mandala.icn b/ipl/gprogs/mandala.icn new file mode 100644 index 0000000..4ccbb37 --- /dev/null +++ b/ipl/gprogs/mandala.icn @@ -0,0 +1,80 @@ +############################################################################ +# +# File: mandala.icn +# +# Subject: Program to draw mandala design +# +# Author: Ralph E. Griswold +# +# Date: September 13, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws "mandala" patterns. +# +# The following options are supported: +# +# -g run continuously; ignore user events; default: process user +# events +# -l i limit on number of iterations, default 2 ^ 10 +# -n i maximum number of points, default 50 +# -s i size of window (width/height); default 256 +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: gobject, interact, joinpair, options, wopen +# +############################################################################ + +link gobject +link interact +link joinpair +link options +link wopen + +procedure main(args) + local i, j, k, angle, incr, xpoint, ypoint, size, radius, opts + local extent, max, limit, run, points + + opts := options(args, "gl+n+s+") + + extent := \opts["s"] | 256 + limit := \opts["l"] | (2 ^ 10) + max := \opts["n"] | 50 + run := opts["g"] + + radius := extent / 2 + + WOpen("label=mandala", "width=" || extent, "height=" || extent, + "bg=light gray", "dx=" || (extent / 2), "dy=" || (extent / 2)) | + ExitNotice("Cannot open window.") + + every 1 to limit do { + i := ?max + if i < 4 then i+:= 3 + ?10 # too few doesn't work well ... + points := list(i) + angle := 0.0 + incr := 2 * &pi / i + every j := 1 to i do { + points[j] := Point(radius * cos(angle), radius * sin(angle)) + angle +:= incr + } + joinpair(points, points) + if /run then repeat case Event() of { + "q": exit() + "s": snapshot() + "n": break + } + WDelay(1000) + EraseArea() + } + +end diff --git a/ipl/gprogs/mandel1.icn b/ipl/gprogs/mandel1.icn new file mode 100644 index 0000000..fb41d77 --- /dev/null +++ b/ipl/gprogs/mandel1.icn @@ -0,0 +1,67 @@ +############################################################################ +# +# File: mandel1.icn +# +# Subject: Program to display the Mandelbrot set +# +# Author: Ralph E. Griswold +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is a barebones version of a display of the Mandelbrot set. It +# has deliberately been left simple and free of options so that the +# basic idea is clear and so that it can be used as the basis of +# more capable versions. +# +# This program is based on material given in "Chaos, Fractals, +# and Dynamics", Robert L. Devaney, Addison-Wesley, 1990. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main() + local size, real_size, i, j, c1, c2, x, y, n, x1, y1, limit, extent + + size := 300 + extent := 4.0 / size + + limit := 30 + + WOpen("label=mandel", "height=" || size, "width=" || size) | + stop("*** cannot open window") + + every i := 1 to size do { + every j := 1 to size / 2 do { + c1 := -2 + i * extent + c2 := 2 - j * extent + x := c1 + y := c2 + every 1 to limit do { # see what the orbit is + x1 := x ^ 2 - y ^ 2 + c1 + y1 := 2 * x * y + c2 + if (x1 ^ 2 + y1 ^ 2) > 4 then break next + x := x1 + y := y1 + } + DrawPoint(i, j, i, size - j) + } + } + + Event() + +end diff --git a/ipl/gprogs/mandel2.icn b/ipl/gprogs/mandel2.icn new file mode 100644 index 0000000..e9a5371 --- /dev/null +++ b/ipl/gprogs/mandel2.icn @@ -0,0 +1,162 @@ +############################################################################ +# +# File: mandel2.icn +# +# Subject: Program to draw the Mandelbrot set +# +# Author: Roger Hare +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws portions of the Mandelbrot set according to the values +# input # on the command line. The method is that described in the articles by +# Dewdney # in the Computer Recreations column of Scientific American in August +# '85, # October '87 and February '89. +# +# I have problems with colours (not enough of 'em!), so I have used alternated +# black and white. Those with decent X-terminals will be able to do far +# better than me. +# +# The program certainly doesn't display images as striking as those seen +# in publications. Perhaps the scaling of the value of k needs to be +# different? All suggestions gratefully received. +# +# It is possible to speed things up by displaying the points row by row +# rather than randomly, but as the program is resident in the 100 cycle +# iteration most of the time, this is only ~5% speed-up. Not really +# worth it. +# +# One of Dewdney's articles mentions other methods to speed things up - I +# will search out the algorithms one of these days... +# +# Usage is - xmand startr startc size n & +# +# where: +# +# startr, startc are the co-ordinates of the lower left hand corner of the +# area of the complex plane to be displayed +# size is the size of the (square) area of the complex plane to be displayed +# n is the number of pixels into which size is to be divided for display +# purposes +# +# For example - xmand -1.5 -1.25 2.5 400 & +# +# will display the Mandelbrot set in the 2.5x2.5 region of the complex plane +# whose s-w corner is -1.5-i1.25. The display will be 400x400 pixels. +# +# The program has been tested on a Sun 4 using the Icon compiler, and +# on a Sequent Symmetry running Version 5 Unix using both the +# compiler and translator. +# +############################################################################ +# +# Links: random, wopen +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ + +link random +link wopen + +procedure main(args) + local a, b, c, colours, coords, events, gap, i, k, n, r, size + local startc, startr, t, x, xmand, y + +# check the number of arguments - if it's not 4, select 4 arbitrary values +if *args == 4 +then {startr:=args[1] + startc:=args[2] + size:=args[3] + n:=args[4] + n:=integer(n)} +else {startr:=-1.5 + startc:=-1.25 + size:=2.5 + n:=200} + +# set max size to 400 +if (n>400) then n:=400 + +# calculate 'size' of each pixel +gap:=size/n + +# open window +xmand:=WOpen("label=xmand", "height="||n+40, + "width="||n+40) | stop("Can't open xmand") + +# set colours to be 5 cycles of alternating black & white - this for the +# benefit of those with monochrome screens, or (like me!), a crummy palette. +colours:=["black","white"] +colours:=colours|||colours|||colours|||colours|||colours + +# write image info in window +GotoXY(xmand,20,35+n) +writes(xmand,startr," ",startc," ",size," ",n) + +# this bit coupled with counting y *downwards* later, effectively means that +# the image in the X-window is 'right way up' for those who live in a +# cartesian world. +startc+:=size + +# set up co-ordinates, one for every point in the display and randomize +coords:=list(n*n,0) +every i:=1 to n*n +do coords[i]:=i-1 +randomize() +every !coords:=:?coords + +# main loop +every i := 1 to n*n +do {t:=get(coords) + +# compute random x,y value from co-ordinate + x:=(t/n) + y:=(t%n) + +# compute value of this x,y point in complex plane - count downwards in +# y direction to get image 'right way up' + r:=startr+x*gap + c:=startc-y*gap + a:=0 + b:=0 + +# and calculate if point is in set or not + every k:=1 to 100 + do {t:=a*a-b*b+r + b:=2*a*b+c + a:=t + if (a*a+b*b) > 4.0 then break} + +# scale final value of k to one of range of colours +# subtract 1 to put in range 0->99; divide by 10 to put in range 0->9 +# add 1 to put in range 1->10 - I have 10 'colours' selected +# this scaling gives fairly unexciting displays, is there a better scaling +# (eg: logarithmic, square root, w.h.y)? + k-:=1 + k/:=10 + k+:=1 + +# and display + Fg(xmand,colours[k]) + DrawPoint(xmand,(x+20),(y+20)) + +# this bit bales out of loop if left button is pressed + if (events:=Pending(xmand)) & (*events > 0) + then if Event(xmand)==&lpress + then break} +WFlush(xmand) + +# just close the window and exit when it is finished +Event(xmand) + +end + diff --git a/ipl/gprogs/mercator.icn b/ipl/gprogs/mercator.icn new file mode 100644 index 0000000..00542f4 --- /dev/null +++ b/ipl/gprogs/mercator.icn @@ -0,0 +1,79 @@ +############################################################################ +# +# File: mercator.icn +# +# Subject: Program to display surface of HLS color cones +# +# Author: Gregg M. Townsend +# +# Date: July 23, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: mercator [window options] [palette] +# +# Mercator displays the surface of the HLS color space (hue, +# lightness, saturation) in something approximating a Mercator +# projection. The white pole is at the top, the black pole is +# at the bottom, and the fully saturated colors run along the +# central equator. +# +# Colors are usually quantized to one of Icon's color palettes, +# with the "c1" palette being the default. Specifying a palette +# of "none" inhibits quantization, generally leading to poor results +# due to color allocation failure. +# +############################################################################ +# +# Calling this a mercator projection is not exactly correct. +# The first problem is that HLS space is a double cone, not a +# sphere, but that can be disregarded by mapping hue to longitude +# and lightness to latitude. Even so, the projection is not truly +# a Mercator projection, but rather another member of the cylindrical +# family: a rectangular, or equidistant cylindrical, projection. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics +# +############################################################################ + +link graphics + +$define Palette "c1" # default palette +$define Size "500,300" # default size + +procedure main(args) + local ww, wh, p, x, y, h, l, dh, dl, hls, c + + Window("size=" || Size, args) + ww := WAttrib("width") # actual window width + wh := WAttrib("height") # actual window height + dh := 360.0 / (ww - 1) # change in hue per pixel + dl := 100.0 / (wh - 1) # change in lightness per pixel + + p := args[1] | Palette + if p == "none" then p := &null + + every x := 0 to ww - 1 do { + h := integer(x * dh) || ":" + every y := 0 to wh - 1 do { + l := 100 - integer(y * dl) + hls := h || l || ":100" + c := HLSValue(hls) + c := PaletteColor(p, PaletteKey(\p, c)) + Fg(c) + DrawPoint(x, y) + } + } + + ZDone() +end diff --git a/ipl/gprogs/mirroror.icn b/ipl/gprogs/mirroror.icn new file mode 100644 index 0000000..ccf7437 --- /dev/null +++ b/ipl/gprogs/mirroror.icn @@ -0,0 +1,55 @@ +############################################################################ +# +# File: mirroror.icn +# +# Subject: Program to mirror images given on command line +# +# Author: Ralph E. Griswold +# +# Date: February 2, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# To get from one image to another, type "n"; to quit, type "q". "s" +# produces a snapshot and "w" writes the name of the file. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, mirror, wopen +# +############################################################################ + +link interact +link mirror +link wopen + +procedure main(args) + local name, win + + every name := !args do { + WOpen("image=" || name, "canvas=hidden") | { + write(&errout, "*** cannot open ", image(name)) + next + } + win := mirror(&window) + WAttrib(win, "canvas=normal", "label=" || name) + repeat case Event(win) of { + "n": break + "s": snapshot(win) + "q": exit() + "w": write(name) # write out file name + } + WClose(&window) + WClose(win) + &window := &null + } + +end diff --git a/ipl/gprogs/moire.icn b/ipl/gprogs/moire.icn new file mode 100644 index 0000000..ee42d10 --- /dev/null +++ b/ipl/gprogs/moire.icn @@ -0,0 +1,98 @@ +############################################################################ +# +# File: moire.icn +# +# Subject: Program to display Moire patterns +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays moire patterns. +# +# The following options are supported: +# +# -g run continuously; ignore user events; default: process user +# events +# -i i initial size, default 50 +# -k i increment, default 1 +# -l i limit on number of iterations, default 2 ^ 10 +# -p s palette, default "c2" +# -s i size of window (width/height); default 256 +# +# This program is based on material given in "FractalVision", +# Dick Oliver, Sams Publishing, 1992, pp. 185-190. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: colrlist, interact, options, wopen +# +############################################################################ + +link colrlist +link interact +link options +link wopen + +procedure main(args) + local extent, size, colors, ncolors, k, x, i, y, j, c, palette + local opts, init, incr, limit, run + + opts := options(args, "gs+p:i+k+l+") + + palette := \opts["p"] | "c2" + extent := \opts["s"] | 256 + init := \opts["i"] | 50 + incr := \opts["k"] | 1 + limit := \opts["l"] | (2 ^ 10) + run := opts["g"] + + size := extent / 2 + + WOpen("label=moire", "height=" || extent, "width=" || extent, + "dx=" || size, "dy=" || size, "bg=light gray") | + ExitNotice("Cannot open window.") + + colors := colrplte(palette) | ExitNotice("Invalid palette.") + ncolors := *colors + + every k := seq(init, incr) \ limit do { + x := k + every i := 0 to size do { + y := x + every j := i to size do { + c := colors[?ncolors] + Fg(c) + DrawPoint( + i, j, + j, i, + j, -i, + i, -j, + -i, -j, + -j, -i, + -j, i, + -i, j + ) + y +:= k + } + x +:= k + } + Fg("black") + if /run then repeat case Event() of { + "q": exit() + "s": snapshot() + "n": break + } + } + +end diff --git a/ipl/gprogs/mover.icn b/ipl/gprogs/mover.icn new file mode 100644 index 0000000..545e233 --- /dev/null +++ b/ipl/gprogs/mover.icn @@ -0,0 +1,98 @@ +############################################################################ +# +# File: mover.icn +# +# Subject: Program to move files from one name to another +# +# Author: Ralph E. Griswold +# +# Date: January 29, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Program to allow interactive moving (renaming) of files. +# +############################################################################ +# +# Requires: UNIX +# +############################################################################ +# +# Links: io, vsetup +# +############################################################################ + +link io +link vsetup + +global names +global root +global vidgets + +procedure main() + + init() + + GetEvents(root, , shortcuts) + +end + +procedure init() + + vidgets := ui() + + root := vidgets["root"] + + names := vidgets["names"] + VSetItems(names, filelist()) + +end + + +procedure file_cb(vidget, value) + + case value[1] of { + "quit @Q": exit() + } + +end + +procedure names_cb(vidget, value, x) + + if /value then return # ignore unselect + + if OpenDialog("Rename:", value) == "Cancel" then fail + if system("mv " || value || " " || dialog_value || + " >/dev/null 2>/dev/null") ~= 0 then { + Notice("Renaming failed.") + fail + } + VSetItems(names, filelist()) + + return + +end + +procedure shortcuts() + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=600,400", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,600,400:",], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["move @M","quit @Q"]], + ["line1:Line:::1,26,598,26:",], + ["names:List:w::26,48,557,335:",names_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/offtiler.icn b/ipl/gprogs/offtiler.icn new file mode 100644 index 0000000..9b81a4d --- /dev/null +++ b/ipl/gprogs/offtiler.icn @@ -0,0 +1,241 @@ +############################################################################ +# +# File: offtiler.icn +# +# Subject: Program to tile images with offset +# +# Author: Ralph E. Griswold +# +# Date: March 14, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces drop repeats and brick patterns. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: io, vsetup +# +############################################################################ + +link io +link vsetup + +global direction +global factor +global height +global subject +global target +global vidgets +global width + +procedure main() + + vidgets := ui() + + direction := "vertical" + factor := 1 + + GetEvents(vidgets["root"], , shortcuts) + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "open @O" : open_image() + "save @S" : save_tile() + "quit @Q" : exit() + } + + return + +end + +procedure parameter_cb(vidget, value) + + case value[1] of { + "direction @D" : set_direction() + "factor @F" : set_factor() + } + + return + +end + +procedure tile_cb() + local incr, i, j, offset + + if /subject then { + Notice("No subject image.") + fail + } + + WClose(\target) + + target := WOpen("label=offset tile", "size=" || (width * factor) || "," || + (height * factor)) | { + Notice("Cannot open target window.") + fail + } + + Raise() + + case direction of { + "vertical" : { + incr := height / factor + every i := -1 to factor do { # columns + offset := i * incr + every j := -1 to factor do { # rows + CopyArea(subject, target, 0, 0, width, height, i * width, + j * height + offset) + } + } + } + "horizontal" : { + incr := width / factor + every i := -1 to factor do { # rows + offset := i * incr + every j := -1 to factor do { # columns + CopyArea(subject, target, 0, 0, width, height, + j * width + offset, i * height) + } + } + } + } + + return + +end + +procedure set_direction() + + repeat { + if SelectDialog("Direction", ["vertical", "horizontal"], direction) == + "Cancel" then fail + direction := dialog_value + check_parameters() | next + return + } + +end + +procedure set_factor() + + repeat { + if TextDialog("Offset factor", , factor) == "Cancel" then fail + factor := (0 < integer(dialog_value[1])) | { + Notice("Invalid factor specification.") + next + } + check_parameters() | next + return + } + +end + +procedure check_parameters() + + case direction of { + "vertical" : { + if (height % factor) ~= 0 then { + Notice("Factor does not evenly divide height.") + fail + } + if factor >= height then { + Notice("Factor too large.") + fail + } + } + "horizontal" : { + if (width % factor) ~= 0 then { + Notice("Factor does not evenly divide width.") + fail + } + if factor >= width then { + Notice("Factor too large.") + fail + } + } + } + + return + +end + +procedure shortcuts(e) + + if &meta then case map(e) of { + "d" : set_direction() + "f" : set_factor() + "o" : open_image() + "q" : exit() + "s" : save_tile() + "t" : tile_cb() + } + + return + +end + +procedure open_image() + + repeat { + if OpenDialog("Open image:") == "Cancel" then fail + WClose(\subject) + subject := WOpen("label=" || dialog_value, "image=" || dialog_value) | { + Notice("Cannot open image.") + next + } + width := WAttrib(subject, "width") + height := WAttrib(subject, "height") + factor := 1 + Raise() + return + } + +end + +procedure save_tile() + local file + + repeat { + if SaveDialog("Save tile:") ~== "Yes" then fail + file := dialog_value + if exists(file) then { + if TextDialog("Overwrite existing file?") == "Cancel" then next + } + WriteImage(target, file) | { + Notice("Cannot write image.") + next + } + return + } + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=200,165", "bg=pale gray"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,200,165:",], + ["file:Menu:pull::0,0,36,21:File",file_cb, + ["open @O","save @S","quit @Q"]], + ["line1:Line:::0,23,200,23:",], + ["parameters:Menu:pull::37,0,78,21:Parameters",parameter_cb, + ["direction @D","factor @F"]], + ["tile:Button:regular::12,36,35,20:tile",tile_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/orbit.icn b/ipl/gprogs/orbit.icn new file mode 100644 index 0000000..3f09ee9 --- /dev/null +++ b/ipl/gprogs/orbit.icn @@ -0,0 +1,58 @@ +############################################################################ +# +# File: orbit.icn +# +# Subject: Program to display quadratic orbit +# +# Author: Ralph E. Griswold +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is a barebones version of a display of the orbit of a quadratic +# equation. It has deliberately been left simple and free of options so +# that the basic idea is clear and so that it can be used as the basis of +# more capable versions. +# +# This program is based on material given in "Chaos, Fractals, +# and Dynamics", Robert L. Devaney, Addison-Wesley, 1990. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main() + local extent, c, i, j, m, x + + extent := 360 + + WOpen("label=orbit", "height=" || extent, "width=" || extent) | + stop("*** cannot open window") + + every i := -320 to 40 do { + x := 0.0 + c := i / 160.0 + m := 160 * (c + 2) + every j := 0 to extent do { + x := x ^ 2 + c + if j < 50 then next # wait for things to take hold + DrawPoint(m, 75 * (2 - x)) + } + } + + Event() + +end diff --git a/ipl/gprogs/painterc.icn b/ipl/gprogs/painterc.icn new file mode 100644 index 0000000..e443a7f --- /dev/null +++ b/ipl/gprogs/painterc.icn @@ -0,0 +1,73 @@ +############################################################################ +# +# File: painterc.icn +# +# Subject: Program to convert Painter color sets to Icon colors +# +# Author: Ralph E. Griswold +# +# Date: January 6, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts color sets from Painter 3 to lists of Icon +# colors. +# +# The lists are saved in files with the base name of the color set and +# the suffix ".clr". +# +############################################################################ +# +# Links: basename +# +############################################################################ + +link basename + +procedure main(args) + local line, file, name, input, output + + every file := !args do { + input := open(file) | { + write(&errout, "*** cannot open ", file) + next + } + name := basename(file, ".txt") + output := open(name || ".clr", "w") | { + write(&errout, "*** cannot open ", name, ".clr") + close(input) + next + } + while line := map(read(input)) do { + line ? { + ="r:" | next + tab(upto(&digits)) + writes(output, 256 * tab(many(&digits)), ",") + tab(find("g:") + 2) | { + write(&errout, "*** invalid data in ", file) + write(&errout, line) + next + } + tab(upto(&digits)) + writes(output, 256 * tab(many(&digits)), ",") + tab(find("b:") + 2) | { + write(&errout, "*** invalid data in ", file) + write(&errout, line) + next + } + tab(upto(&digits)) + writes(output, 256 * tab(many(&digits))) + tab(many(' \t')) + if not pos(0) then write(output, "\t", tab(0)) + else write(output) + } + } + close(input) + close(output) + } + +end diff --git a/ipl/gprogs/palcheck.icn b/ipl/gprogs/palcheck.icn new file mode 100644 index 0000000..be8fbbb --- /dev/null +++ b/ipl/gprogs/palcheck.icn @@ -0,0 +1,69 @@ +############################################################################ +# +# File: palcheck.icn +# +# Subject: Program to check palindromic sentences +# +# Authors: K'vin D'vries and Ralph E. Griswold +# +# Date: August 14, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads lines from standard input. If a line is a +# palindromic sentence (see The Icon Programming Language, 2nd edition, +# p. 58), it is ignored. If it is not a palindromic sentence, it is +# written to a window with the outermost characters that don't match +# highlighted. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen, xcompat +# +############################################################################ + +link wopen +link xcompat + +procedure main() + local normal, highlight, line, s1, s2, i1, i2 + + normal := WOpen("label=palindrome check", "lines=20", "columns=80", + "leading=18") | stop("*** cannot open window") + + highlight := XBind(normal, , "reverse=on") + + while line := read() do { + s1 := map(line) + s2 := reverse(s1) + + i1 := i2 := 1 + + while i1 < *line do { + (i1 := upto(&lcase, s1, i1) & i2 := upto(&lcase, s2, i2)) | break + if s1[i1] ~== s2[i2] then { + line ? { + writes(normal, tab(i1)) + writes(highlight, move(1)) + writes(normal, tab(*line - i2 + 1)) + writes(highlight, move(1)) + write(normal, tab(0)) + } + break + } + i1 +:= 1 + i2 +:= 1 + } + } + + Event(normal) + +end diff --git a/ipl/gprogs/palette.icn b/ipl/gprogs/palette.icn new file mode 100644 index 0000000..fe9ef1e --- /dev/null +++ b/ipl/gprogs/palette.icn @@ -0,0 +1,85 @@ +############################################################################ +# +# File: palette.icn +# +# Subject: Program to display an Icon image palette +# +# Author: Gregg M. Townsend +# +# Date: May 23, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: palette [name] +# +# Palette displays each color available in an image palette along with its +# index character. The default palette is "c1". +# +# Typing a digit (1 to 6) in the window switches the display to the +# corresponding color palette. Typing a "g" selects the "g16" palette. +# +# Typing "l", "o", or "u" toggles the respective drawpalette() flag. +# +# The window can be resized. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, imscolor +# +############################################################################ + +link graphics, imscolor + +global flags + +procedure main(args) + local p, nw, nh, w, h, e + + flags := "l" + if args[-1] ? any(&letters) then + p := args[-1] + else + p := "c1" + + PaletteChars(p) | stop(&progname, ": palette ", p, " not found") + + Window("width=125", "height=250", "font=lucidasans-bold-12", + "label=" || p, args) + + &error := 1 + WAttrib("resize=on") + &error := 0 + + draw(p) + while e := Event() do case e of { + QuitEvents(): break + !"123456": draw(p := "c" || e) + "g": draw(p := "g16") + &lpress | &ldrag: writes(pickpalette(p, &x, &y) | "~") & flush(&output) + &resize: draw(p) + !"lou": { + if flags ? find(e) then + flags := string(flags -- e) + else + flags ||:= e + draw(p) + } + } +end + + +procedure draw(p) # draw palette, etc. + WAttrib("label=" || p) + EraseArea() + drawpalette(p, , , , , flags) | + write(&errout, " could not get all colors of ", p, " palette") + return +end diff --git a/ipl/gprogs/pat2gif.icn b/ipl/gprogs/pat2gif.icn new file mode 100644 index 0000000..46a1402 --- /dev/null +++ b/ipl/gprogs/pat2gif.icn @@ -0,0 +1,48 @@ +############################################################################ +# +# File: pat2gif.icn +# +# Subject: Program to convert bi-level pattern to GIF +# +# Author: Ralph E. Griswold +# +# Date: November 30, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Names ofs BLP are given on the command line. The GIFs have a +# corresponding basename. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, patutils, wopen +# +############################################################################ + +link basename +link patutils +link wopen + +procedure main(args) + local matrix, ims, input, file + + while file := get(args) do { + input := open(file) | stop("cannot open ", file) + ims := read(input) | stop("empty BLP") + matrix := pat2rows(ims) | stop("*** invalid pattern") + WOpen("size=" || *matrix[1] || "," || *matrix, "canvas=hidden") | + stop("*** cannot open window") + DrawImage(0, 0, ims) + WriteImage(basename(file, ".blp") || ".gif") + close(input) + } + +end diff --git a/ipl/gprogs/patfetch.icn b/ipl/gprogs/patfetch.icn new file mode 100644 index 0000000..2c32f51 --- /dev/null +++ b/ipl/gprogs/patfetch.icn @@ -0,0 +1,85 @@ +############################################################################ +# +# File: patfetch.icn +# +# Subject: Program to extract patterns from a file +# +# Author: Ralph E. Griswold +# +# Date: July 22, 1993 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program accepts a list of integer specifications for patterns +# as the appear in order in a file of patterns. The selected patterns +# are written to standard output, but not until the end of the input +# specifications. The name of the pattern file is specified on the +# command line. +# +# Each line of input can be a comma-separated string of either integers +# or integer ranges. Blanks after commas are tolerated. An example of +# input is: +# +# 1-3, 5 +# 10 +# 13-17 +# 8 +# +# which specifies the patterns 1, 2, 3, 5, 8, 10, 13, 14, 15, 16, and 17. +# +# Note that the integers need not be in order, although within a range, +# the lower bound must precede the upper bound. +# +############################################################################ +# +# Links: patutils +# +############################################################################ + +link patutils + +procedure main(args) + local file, input, i, hitlist, patlist, spec, lo, hi, subspec + + file := args[1] | stop("*** no pattern list specified") + + input := open(file) | stop(" *** cannot open input file") + + hitlist := set() # construct set of indices to remove + + while spec := read() do { + spec ? { + while subspec := tab(upto(',') | 0) do { + if insert(hitlist, integer(subspec)) then { # integer + move(1) | break + tab(many(' ')) + } + else { + subspec ? { + lo := tab(many(&digits)) & + ="-" & + hi := tab(many(&digits)) & + lo <= hi & + pos(0) | write(&errout, "*** bad specification") + every insert(hitlist, 0 < integer(lo to hi)) + } + move(1) | break + tab(many(' ')) + } + } + } + } + + patlist := [] # read in list of patterns + + while put(patlist, readpatt(input)) + + every i := !sort(hitlist) do { # write out selected patterns + write(patlist[i]) | write(&errout, "*** ", i, " out of bounds") + } + +end diff --git a/ipl/gprogs/penelope.icn b/ipl/gprogs/penelope.icn new file mode 100644 index 0000000..55861e3 --- /dev/null +++ b/ipl/gprogs/penelope.icn @@ -0,0 +1,1256 @@ +############################################################################ +# +# File: penelope.icn +# +# Subject: Program to edit graphic patterns +# +# Authors: Ralph E. Griswold and Gregg M. Townsend +# +# Date: May 25, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This application provides a variety of facilities for creating and +# editing graphic pattern specifications. For a complete description, +# see IPD234: +# http://www.cs.arizona.edu/icon/docs/ipd234.htm +# +############################################################################ +# +# Requires: Version 9 graphics with 32-column tiles +# +############################################################################ +# +# Links: sort, patxform, vdialog, vsetup, dialog, wopen, xcompat +# +############################################################################ + +link sort +link patxform +link vdialog +link vsetup +link dialog +link wopen +link xcompat + +$define MaxCell 24 # maximum size of grid cell + +$define GridSize (32 * 8) # size of area for edit grid +$define GridXoff (32 * 5) # x offset of grid area +$define GridYoff (32 * 2 + 6) # y offset of grid area + +$define PattXoff (32 * 14) # x offset of pattern area +$define PattYoff (32 * 2) # y offset of pattern area +$define PattWidth (32 * 8) # width of pattern area +$define PattHeight (32 * 8) # heigth of pattern area + +$define IconSize 16 # size of button icons + +$define XformXoff (16 * 2) # x offset of xform area +$define XformYoff (16 * 4) # y offset of xform area + +$define SymmetXoff (16 * 10) # x offset of symmetry area +$define SymmetYoff (16 * 23) # y offset of symmetry area + +$define InfoLength 40 # length of lines in info box + +global allxform # transform-all switch +global hbits # number of bits horizontally +global vbits # number of bits veritcally +global rows # row repesentation of tile +global old_pat # old pattern for undo +global cellsize # size of cell in edit grid +global pattgc # graphic context for pattern +global bordergc # border for tile/pattern +global viewgc # clipping area for viewing +global mode # pattern/tile display mode +global zoom # tile zoom factor +global loadname # name of loaded pattern file +global plist # pattern list +global pindex # index in pattern list +global list_touched # list modification switch +global tile_touched # tile modification switch +global blank_pat # 8x8 blank tile +global response # switch for save dialog +global sym_state # drawing state +global sym_image_current # current drawing images +global sym_image_next # next drawing images +global symmetries # general symmetry state + +global flip_right # icon for right flip +global flip_left # icon for left flip +global flip_vert # icon for vertical flip +global flip_horiz # icon for horizontal flip +global rotate_90 # icon for 90-degree rotation +global rotate_m90 # icon for -90-degree rotation +global rotate_180 # icon for 180-degree rotation +global ident # icon for identity +global hi_ident # highlighted icon for identity +global hi_left # highlighted icon for l-flip +global hi_right # highlighted icon for r-flip +global hi_vert # highlighted icon for v-flip +global hi_horiz # highlighted icon for h-flip +global hi_rot_90 # highlighted icon for 90-rot +global hi_rot_m90 # highlighted icon for -90 rot +global hi_rot_180 # highlighted icon for 180 rot + +global MaxPatt # maximum width for patterns + +record pattrec(tile, note) + +procedure main(args) + local vidgets, e, i, j, x, y, v, h, input, mdigits + +# Initial state + + mdigits := '-' ++ &digits + mode := 1 # initially pattern mode + zoom := 1 # initially 1:1 + symmetries := 0 # initially no symmetries + allxform := &null # initially not all xforms + + sym_state := [ # initially no symmetries + [1, -1, -1, -1], + [-1, -1, -1, -1] + ] + + blank_pat := "8,#0000000000000000" # 8x8 blank tile + + list_touched := &null # pristine state + tile_touched := &null + +# Conservative assumption that only X can handle tiles up to 32 wide + + MaxPatt := if &features == "X Windows" then 32 else 8 + +# Set up initial pattern list + + if loadname := args[1] then { + input := open(loadname) | stop("*** cannot open ", loadname) + if load_file(input) then old_pat := rows2pat(rows) + else stop("*** no patterns in ", loadname) + } + else { + loadname := "untitled.tle" + rows := pat2rows(blank_pat) + old_pat := rows2pat(rows) + plist := [pattrec(rows2pat(rows), "")] + pindex := 1 + } + +# Set up vidgets + + vidgets := ui(, vecho) + + WAttrib("label=" || loadname) + +# Set up graphic contexts + + pattgc := XBind(&window, "fillstyle=textured") # for patterns + bordergc := XBind(&window, "fg=red") # for border + viewgc := XBind(&window) # for tile view + Clip(viewgc, PattXoff, PattYoff, PattWidth, PattHeight) + Clip(bordergc, PattXoff - 1, PattYoff - 1, PattWidth + 2, PattHeight + 2) + +# Assign and draw the icons + + icons() + +# Initial and toggled editing images + + sym_image_next := [ + [ident, hi_rot_90, hi_rot_m90, hi_rot_180], + [hi_right, hi_left, hi_vert, hi_horiz] + ] + sym_image_current := [ + [hi_ident, rotate_90, rotate_m90, rotate_180], + [flip_right, flip_left, flip_vert, flip_horiz] + ] + +# Initial setup of grid and view areas + + setup() | stop("*** cannot set up pattern") + +# Enter event loop + + GetEvents(vidgets["root"], , shortcuts) + +end + +############################################################################ +# +# Callback procedures +# +############################################################################ + +# file menu + +procedure file_cb(vidget, value) + + case value[1] of { + "load @L" : load() + "save @S" : save() + "save as" : save_as() + "read @R" : read_tile() + "write @W" : write_tile() + "quit @Q" : quit() + } + + return + +end + +# editing grid + +procedure grid_cb(vidget, e) + local x, y, i, j + + if e === (&lpress | &rpress | &ldrag | &rdrag) then { + j := (&x - GridXoff) / cellsize + i := (&y - GridYoff) / cellsize + if j < 0 | j >= hbits | i < 0 | i >= vbits then return + if e === (&lpress | &ldrag) then setbit(i, j, "1") + else setbit(i, j, "0") + tile_touched := 1 + } + + return + +end + +# list menu + +procedure list_cb(vidget, value) + local i + + case value[1] of { + "clear" : { # should request confirmation + plist := [pattrec(blank_pat, "")] + } + "reverse" : { + every i := 1 to *plist / 2 do + plist[i] :=: plist[-i] + } + "sort" : { + refresh_tile() + plist := isort(plist, case value[2] of { + "by size": tile_size + "by bits": tile_bits + "by notes": tile_note + }) + } + } + + pindex := 1 + + rows := pat2rows(plist[1].tile) + old_pat := rows2pat(rows) + + list_touched := 1 + + return setup() + +end + +# Penelope logo + +procedure logo_cb(vidgets, event) + + if event === (&lpress | &mpress | &rpress) then + Notice("Penelope", "Version 1.1", + "Ralph E. Griswold and Gregg M. Townsend") + + return + +end + +# note menu + +procedure note_cb(vidget, value) + local result, note, i + + case value[1] of { + "edit @E" : edit_tile() + "find @F" : find_tile() + } + + return + +end + +# symmetry buttons + +procedure symmet_cb(vidget, e) + local col, row, symcount + + if e === (&lpress | &rpress | &mpress) then { + col := (&x - SymmetXoff) / IconSize + 1 + row := (&y - SymmetYoff) / IconSize + 1 + sym_state[row, col] *:= -1 + sym_image_current[row, col] :=: sym_image_next[row, col] + place(SymmetXoff, SymmetYoff, col - 1, row - 1, + sym_image_current[row, col]) + symcount := 0 + every symcount +:= !!sym_state + if symcount = -8 then + Notice("No drawing mode enabled; pattern cannot be edited") + else if (sym_state[1, 1] = 1) & (symcount = -6) then symmetries := 0 + else symmetries := 1 + + return + } + + fail + +end + +# tile menu + +procedure tile_cb(vidget, value) + local result + + case value[1] of { + "next @N" : next_tile() + "previous @P" : previous_tile() + "goto @G" : goto_tile() + "first" : { + refresh_tile() + pindex := 1 + rows := pat2rows(plist[pindex].tile) + tile_touched := 1 + return setup() + } + "last" : { + refresh_tile() + pindex := *plist + rows := pat2rows(plist[pindex].tile) + tile_touched := 1 + return setup() + } + "copy C" : copy_tile() + "revert" : { + rows := pat2rows(plist[pindex].tile) + return setup() + } + "delete D" : delete_tile() + "new" : { + case Dialog("New:", ["width", "height"], [*rows[1], *rows], 3, + ["Okay", "Cancel"]) of { + "Cancel" : fail + "Okay" : { + icheck(dialog_value) | fail + refresh_tile() + rows := list(dialog_value[2], repl("0", dialog_value[1])) + put(plist, pattrec(rows2pat(rows), "")) + pindex := *plist + tile_touched := 1 + return setup() + } + } + } + "info I" : tile_info() + } + + return + +end + +# view menu + +procedure view_cb(vidget, value) + static old_mode, old_zoom + + old_mode := mode + old_zoom := zoom + + case value[1] of { + "pattern" : mode := 1 + "tile" : mode := &null + "tile zoom" : { + mode := &null + case value[2] of { + "1:1" : zoom := 1 + "2:1" : zoom := 2 + "4:1" : zoom := 4 + "8:1" : zoom := 8 + } + } + } + + if (mode ~=== old_mode) | (zoom ~=== old_zoom) then { + DrawRectangle(bordergc, PattXoff - 1, PattYoff - 1, + PattWidth + 1, PattHeight + 1) + EraseArea(PattXoff - 1, PattYoff - 1, PattWidth + 1, PattHeight + 1) + return setup() + } + + return + +end + +# transformation buttons + +procedure xform_cb(vidget, e) + local col, row, save_pindex + + if e === (&lpress | &rpress | &mpress) then { + old_pat := rows2pat(rows) + col := (&x - XformXoff) / IconSize + row := (&y - XformYoff) / IconSize + + if &shift then { + refresh_tile() + save_pindex := pindex + every pindex := 1 to *plist do { + rows := pat2rows((plist[pindex]).tile) + rows := xform(col, row) + (plist[pindex]).tile := rows2pat(rows) + allxform := 1 # all being done + } + allxform := &null # one being done + list_touched := 1 + pindex := save_pindex + rows := pat2rows(plist[pindex].tile) + } + else rows := xform(col, row) | fail + + return setup() + + } + +end + +############################################################################ +# +# Support procedures +# +############################################################################ + +# clear bits on current tile + +procedure clear_tile() + + rows := list(vbits, repl("0", hbits)) + + grid() + + drawpat() + + return + +end + +# copy current tile + +procedure copy_tile() + + refresh_tile() + put(plist, pattrec(old_pat := rows2pat(rows), "")) + rows := pat2rows(old_pat) + pindex := *plist + + list_touched := 1 + + return setup() + +end + +# delete current tile + +procedure delete_tile() + # should ask confirmation + if *plist = 1 then plist := [pattrec(blank_pat, "")] + else { + plist := plist[1 : pindex] ||| plist[pindex + 1 : 0] + if pindex > *plist then pindex := *plist + } + + rows := pat2rows((plist[pindex]).tile) + + list_touched := 1 + + return setup() + +end + +# draw view area + +procedure drawpat() + + if \mode then { # draw pattern + DrawRectangle(bordergc, PattXoff - 1, PattYoff - 1, + PattWidth + 1, PattHeight + 1) + Pattern(pattgc, rows2pat(rows)) + FillRectangle(pattgc, PattXoff, PattYoff, PattWidth, PattHeight) + } + else { # draw tile + EraseArea(PattXoff - 1, PattYoff - 1, PattWidth + 2, PattHeight + 2) + DrawRectangle(bordergc, PattXoff - 1, PattYoff - 1, + (*rows[1] * zoom) + 1, (*rows * zoom) + 1) + DrawRows(viewgc, PattXoff, PattYoff, rows, zoom) + } + return + +end + +# edit annotation on current tile + +procedure edit_tile() + local result + + case Dialog("Edit:", "note", [plist[pindex].note], 80, + ["Okay", "Cancel"]) of { + "Cancel": fail + "Okay": { + plist[pindex].note := dialog_value[1] || " " + list_touched := 1 + } + } + + return + +end + +# find tile with annotation + +procedure find_tile() + local note, i + + case Dialog("Find:", "note", "", 80, ["Okay", "Cancel"]) of { + "Cancel": fail + "Okay": { + note := dialog_value[1] || " " + every i := ((pindex + 1 to *plist) | (1 to *pindex)) do + plist[i].note ? { + if find(note) then { + pindex := i + rows := pat2rows(plist[pindex].tile) + return setup() + } + } + } + } + + Notice("Not found") + + fail + +end + +# go to specified tile + +procedure goto_tile() + local i + + case Dialog("Go to:","#", 1, 5, ["Okay", "Cancel"]) of { + "Cancel": fail + "Okay": i := integer(dialog_value[1]) | { + Notice("Invalid specification") + fail + } + } + refresh_tile() + if i <= 0 then i +:= *plist + 1 + if i <= i <= *plist + 1 then { + pindex := i + old_pat := rows2pat(rows) + rows := pat2rows(plist[pindex].tile) + return setup() + } + else { + Notice("Index out of bounds") + fail + } + +end + +# draw editing grid + +procedure grid() + local x, y + + EraseArea(GridXoff, GridYoff, GridSize - 15, GridSize - 15) + + every x := 0 to hbits * cellsize by cellsize do + DrawLine(GridXoff + x, GridYoff, GridXoff + x, + GridYoff + vbits * cellsize) + every y := 0 to vbits * cellsize by cellsize do + DrawLine(GridXoff, GridYoff + y, GridXoff + hbits * cellsize, + y + GridYoff) + + return + +end + +# check for valid integers + +procedure icheck(values) + local i + + every i := !values do + if not(integer(i)) | (i < 0) then { + Notice("Invalid value") + fail + } + + return + +end + +# assign and draw icons + +procedure icons() + local shift_up, shift_left, shift_right, shift_down, pixmap + local clear, invert, scramble, trim, enlarge, resize, crop + + pixmap := XBind(, , "width=32", "height=32", "fillstyle=masked") + + Pattern(pixmap, "32,#7fffffff421f843f421f843f421f843f421f843f7fffff_ + ff421084214210842142108421421084217fffffff4210fc21_ + 4210fc214210fc214210fc217fffffff421087e1421087e142_ + 1087e1421087e17fffffff7e10fc217e10fc217e10fc217e10_ + fc217fffffff7e10843f7e10843f7e10843f7e10843f7fffff_ + ff00000000") # Penelope logo + + FillRectangle(pixmap, 0, 0, 32, 32) + + CopyArea(pixmap, &window, 0, 0, 32, 32, 26, 373) + + Uncouple(pixmap) + + shift_up := "16,#3ffe6003408141c143e140814081408140814081408140_ + 81408160033ffe0000" + shift_left := "16,#3ffe6003400140014001401140195ffd40194011400140_ + 01400160033ffe0000" + shift_right := "16,#3ffe600340014001400144014c015ffd4c014401400140_ + 01400160033ffe0000" + shift_down := "16,#3ffe60034081408140814081408140814081408143e141_ + c1408160033ffe0000" + flip_left := "16,#3ffe600340014079403940394049408149014e014e014f_ + 01400160033ffe0000" + flip_right := "16,#3ffe600340014f014e014e014901408140494039403940_ + 79400160033ffe0000" + flip_vert := "16,#3ffe6003408141c143e14081408140814081408143e141_ + c1408160033ffe0000" + flip_horiz := "16,#3ffe600340014001400144114c195ffd4c194411400140_ + 01400160033ffe0000" + rotate_90 := "16,#3ffe6003400140f141014201420142014f814701420140_ + 01400160033ffe0000" + rotate_m90 := "16,#3ffe600340014781404140214021402140f94071402140_ + 01400160033ffe0000" + rotate_180 := "16,#3ffe6003400141c140214011401140114111432147c143_ + 01410160033ffe0000" + clear := "16,#3ffe600340014001400140014001400140014001400140_ + 01400160033ffe0000" + invert := "16,#3ffe60ff40ff40ff40ff40ff40ff7fff7f817f817f817f_ + 817f817f833ffe0000" + scramble := "16,#3ffe60034c014c0d418d41814001403159b1598140194c_ + 194c0160033ffe0000" + trim := "16,#3ffe60134011407d40394011400140fd48854c857e854c_ + 8548fd60033ffe0000" + enlarge := "16,#3ffe6083418143fd418148815c017efd48854885488548_ + 8548fd60033ffe0000" + resize := "16,#3ffe6093419943fd419948915c017efd488548857e855c_ + 8548fd60033ffe0000" + crop := "16,#3ffe60034011401147fd441144114411441144115ff144_ + 01440160033ffe0000" + + ident := "16,#3ffe6003400140014001400141c141c141c14001400140_ + 01400160033ffe0000" + + hi_ident := "16,#00001ffc3ffe3ffe3ffe3ffe3e3e3e3e3e3e3ffe3ffe3f_ + fe3ffe1ffc00000000" + hi_rot_90 := "16,#00001ffc3ffe3f0e3efe3dfe3dfe3dfe307e38fe3dfe3f_ + fe3ffe1ffc00000000" + hi_rot_m90 := "16,#00001ffc3ffe387e3fbe3fde3fde3fde3f063f8e3fde3f_ + fe3ffe1ffc00000000" + hi_rot_180 := "16,#00001ffc3ffe3e3e3fde3fee3fee3fee3eee3cde383e3c_ + fe3efe1ffc00000000" + hi_right := "16,#00001ffc3ffe30fe31fe31fe36fe3f7e3fb63fc63fc63f_ + 863ffe1ffc00000000" + hi_left := "16,#00001ffc3ffe3f863fc63fc63fb63f7e36fe31fe31fe30_ + fe3ffe1ffc00000000" + hi_vert := "16,#00001ffc3f7e3e3e3c1e3f7e3f7e3f7e3f7e3f7e3c1e3e_ + 3e3f7e1ffc00000000" + hi_horiz := "16,#00001ffc3ffe3ffe3ffe3bee33e6200233e63bee3ffe3f_ + fe3ffe1ffc00000000" + +# now place the images + + place(XformXoff, XformYoff, 1, 0, shift_up) + place(XformXoff, XformYoff, 0, 1, shift_left) + place(XformXoff, XformYoff, 2, 1, shift_right) + place(XformXoff, XformYoff, 1, 2, shift_down) + place(XformXoff, XformYoff, 0, 4, flip_right) + place(XformXoff, XformYoff, 0, 5, flip_left) + place(XformXoff, XformYoff, 1, 4, flip_vert) + place(XformXoff, XformYoff, 1, 5, flip_horiz) + place(XformXoff, XformYoff, 0, 7, rotate_90) + place(XformXoff, XformYoff, 0, 8, rotate_m90) + place(XformXoff, XformYoff, 1, 7, rotate_180) + place(XformXoff, XformYoff, 0, 10, clear) + place(XformXoff, XformYoff, 1, 10, invert) + place(XformXoff, XformYoff, 2, 10, scramble) + place(XformXoff, XformYoff, 0, 12, trim) + place(XformXoff, XformYoff, 1, 12, enlarge) + place(XformXoff, XformYoff, 2, 12, resize) + place(XformXoff, XformYoff, 0, 14, crop) + + place(SymmetXoff, SymmetYoff, 0, 0, hi_ident) + place(SymmetXoff, SymmetYoff, 1, 0, rotate_90) + place(SymmetXoff, SymmetYoff, 2, 0, rotate_m90) + place(SymmetXoff, SymmetYoff, 3, 0, rotate_180) + place(SymmetXoff, SymmetYoff, 0, 1, flip_right) + place(SymmetXoff, SymmetYoff, 1, 1, flip_left) + place(SymmetXoff, SymmetYoff, 2, 1, flip_vert) + place(SymmetXoff, SymmetYoff, 3, 1, flip_horiz) + + return + +end + +# invert bits on current pattern + +procedure invert() + + rows := pinvert(rows) + + return + +end + +# load tile list + +procedure load() + local input + + refresh_tile() + + if \list_touched then { # check to see if list should be saved + case SaveDialog(, loadname) of { + "Yes": { + loadname := dialog_value + save() + } + } + } + + repeat { + case OpenDialog("Load: ") of { + "Okay": { + loadname := dialog_value + if input := open(loadname) then break + else { + Notice("Can't open " || loadname) + next + } + } + "Cancel": fail + } + } + load_file(input) | { + Notice("No patterns in file") + fail + } + WAttrib("label=" || loadname) + list_touched := &null + + return setup() + +end + +# load from file + +procedure load_file(input) + local line + + plist := [] + while put(plist, read_pattern(input)) + close(input) + pindex := 1 + rows := pat2rows(plist[pindex].tile) | fail + + return + +end + +# go to next tile + +procedure next_tile() + + refresh_tile() + rows := pat2rows(plist[pindex + 1].tile) | { + Notice("No next tile") + fail + } + + pindex +:= 1 + + return setup() + +end + +# place icon + +procedure place(xoff, yoff, col, row, pattern) + + Pattern(pattgc, pattern) + FillRectangle(pattgc, xoff + col * IconSize, + yoff + row * IconSize, IconSize, IconSize) + + return + +end + +# go to previous tile + +procedure previous_tile() + + rows := pat2rows(plist[pindex - 1].tile) | { + Notice("No previous tile") + fail + } + + refresh_tile() + pindex -:= 1 + + return setup() + +end + +# terminate session + +procedure quit() + local result + + refresh_tile() + + if \list_touched then { + case SaveDialog() of { + "Cancel": fail + "No": exit() + "Yes": { + loadname := dialog_value + save() + } + } + } + + exit() + +end + +# read pattern specification + +procedure read_pattern(file) + local line + + line := readpattline(file) | fail + + return pattrec(legaltile(getpatt(line)), getpattnote(line)) + +end + +# read and add tile to tile list + +procedure read_tile() + + refresh_tile() + put(plist, read_pattern(&input)) | fail + pindex := *plist + rows := pat2rows((plist[pindex]).tile) + + list_touched := 1 + + return setup() + +end + +# refresh tile in list + +procedure refresh_tile() + + if \tile_touched := &null then { + plist[pindex].tile := rows2pat(rows) + list_touched := 1 + } + + return + +end + +# save tile list + +procedure save() # should ask if file is to be saved + local output + + refresh_tile() + + if \list_touched then { + output := open(loadname, "w") | { + Notice("Can't open " || loadname) + fail + } + every write_pattern(output, !plist) + close(output) + list_touched := &null + } + + return + +end + +# save tile list in new file + +procedure save_as() + local output + + refresh_tile() + + repeat { + case OpenDialog("Save as:") of { + "Okay": { + if output := open(dialog_value, "w") then break else + Notice("Can't open " || dialog_value) + } + "Cancel": fail + } + } + every write_pattern(output, !plist) + close(output) + + loadname := dialog_value + WAttrib("label=" || loadname) + + list_touched := &null + + return + +end + +# scramble bits of current tile + +procedure bscramble() + + rows := pscramble(rows, "b") + + return + +end + +# set bits of tile + +procedure setbit(i, j, c) + local x, y, xu, yu, xv, yv, xt, yt, action + + if (symmetries = 0) & (rows[i + 1, j + 1] == c) then return # optimization + + x := GridXoff + j * cellsize + 1 # the selected cell itself + y := GridYoff + i * cellsize + 1 + xt := GridXoff + i * cellsize + 1 + yt := GridYoff + j * cellsize + 1 + + i +:= 1 # for computational convenience + j +:= 1 + + xu := GridXoff + (hbits - j) * cellsize + 1 # opposite cells + yu := GridYoff + (vbits - i) * cellsize + 1 + xv := GridXoff + (hbits - i) * cellsize + 1 + yv := GridYoff + (vbits - j) * cellsize + 1 + + action := if c = 1 then FillRectangle else EraseArea + + if sym_state[1, 1] = 1 then { # cell itself + rows[i, j] := c + action(x, y, cellsize - 1, cellsize - 1) + } + if sym_state[1, 2] = 1 then { # 90 degrees + if rows[j, -i] := c then # may be out of bounds + action(xv, yt, cellsize - 1, cellsize - 1) + } + if sym_state[1, 3] = 1 then { # -90 degrees + if rows[-j, i] := c then # may be out of bounds + action(xt, yv, cellsize - 1, cellsize - 1) + } + if sym_state[1, 4] = 1 then { # 180 degrees + rows[-i, -j] := c + action(xu, yu, cellsize - 1, cellsize - 1) + } + if sym_state[2, 1] = 1 then { # left diagonal + if rows[j, i] := c then # may be out of bounds + action(xt, yt, cellsize - 1, cellsize - 1) + } + if sym_state[2, 2] = 1 then { # right diagonal + if rows[-j, -i] := c then # may be out of bounds + action(xv, yv, cellsize - 1, cellsize - 1) + } + if sym_state[2, 3] = 1 then { # vertical + rows[-i, j] := c + action(x, yu, cellsize - 1, cellsize - 1) + } + if sym_state[2, 4] = 1 then { # horizontal + rows[i, -j] := c + action(xu, y, cellsize - 1, cellsize - 1) + } + + drawpat() + + return + +end + +# set up editing grid and view area + +procedure setup() + local i, j + + hbits := *rows[1] + vbits := *rows + + if (hbits | vbits) > 80 then { # based on cell size >= 3 + Notice("Dimensions too large") + fail + } + if hbits > MaxPatt then mode := &null # too large for pattern + + cellsize := MaxCell # cell size on window + cellsize >:= GridSize / (vbits + 4) + cellsize >:= GridSize / (hbits + 4) + + grid() + + every i := 1 to hbits do + every j := 1 to vbits do + if rows[j, i] == "1" then + FillRectangle(GridXoff + (i - 1) * cellsize, + GridYoff + (j - 1) * cellsize, cellsize, cellsize) + + drawpat() + + return + +end + +# keyboard shortcuts + +procedure shortcuts(e) + + if &meta then case map(e) of { + "c" : copy_tile() + "d" : delete_tile() + "e" : edit_tile() + "f" : find_tile() + "g" : goto_tile() + "i" : tile_info() + "l" : load() + "n" : next_tile() + "p" : previous_tile() + "q" : return quit() + "r" : read_tile() + "s" : save() + "u" : undo_xform() + "w" : write_tile() + } + + return + +end + +# return number of bits set in tile for sorting + +procedure tile_bits(x) + + return tilebits(pat2rows(x.tile)) + +end + +# show information about tile + +procedure tile_info() + local line1, line2, line3, line4, pattern, bits, density + + pattern := rows2pat(rows) + bits := tilebits(rows) + density := left(bits / real(*rows[1] * *rows), 6) + + line1 := left(loadname ||" " || pindex || " of " || *plist, InfoLength) + line2 := left(*rows[1] || "x" || *rows || " b=" || bits || " d=" || + density, InfoLength) + line3 := if *pattern > InfoLength then pattern[1+:(InfoLength - 3)] || + "..." else left(pattern, InfoLength) + line4 := left(plist[pindex].note, InfoLength) + + Notice(line1, line2, line3, line4) + + return + +end + +# return annotation of tile for sorting + +procedure tile_note(x) + + return x.note + +end + +# return tile size for sorting + +procedure tile_size(x) + local dims + + dims := tiledim(x.tile) + + return dims.w * dims.h + +end + +# undo transformation + +procedure undo_xform() + + rows := pat2rows(old_pat) + + return setup() + +end + +# write pattern + +procedure write_pattern(file, pattern) + + if *pattern.note = 0 then write(file, pattern.tile) + else write(file, pattern.tile, "\t# ", pattern.note) + + return + +end + +# write tile + +procedure write_tile() + + write_pattern(&output, pattrec(rows2pat(rows), (plist[pindex]).note)) + + return + +end + +# handle transformation + +procedure xform(col, row) + local result + static params + + tile_touched := 1 + + return case col of { + 0: case row of { + 1: pshift(rows, -1, "h") + 4: pflip(rows, "r") + 5: pflip(rows, "l") + 7: protate(rows, 90) + 8: protate(rows, -90) + 10: list(vbits, repl("0", hbits)) + 12: ptrim(rows) + 14: { + if /allxform then { + case Dialog("Crop:", ["left", "right", "top", "bottom"], + 0, 3, ["Okay", "Cancel"]) of { + "Cancel": fail + "Okay": { + icheck(dialog_value) | fail + result := copy(params := dialog_value) + push(result, rows) + pcrop ! result + } + } + } + } + default: fail + } + 1: case row of { + 0: pshift(rows, -1, "v") + 2: pshift(rows, 1, "v") + 4: pflip(rows, "v") + 5: pflip(rows, "h") + 7: protate(rows, 180) + 10: pinvert(rows) + 12: { + if /allxform then { + case Dialog("Enlarge:", ["left", "right", "top", "bottom"], + 0, 3, ["Okay", "Cancel"]) of { + "Cancel": fail + "Okay": { + icheck(dialog_value) | fail + result := copy(params := dialog_value) + push(result, rows) + pborder ! result + } + } + } + } + default: fail + } + 2: case row of { + 1: pshift(rows, 1, "h") + 10: pscramble(rows, "b") + 12: { + if /allxform then { + case Dialog("Center:", ["width", "height"], [*rows[1], *rows], + 3, ["Okay", "Cancel"]) of { + "Cancel": fail + "Okay": { + icheck(dialog_value) | fail + result := copy(params := dialog_value) + push(result, rows) + pcenter ! result + } + } + } + } + default: fail + } + default: fail + } + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=730,420", "bg=pale gray", "label=Penelope"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,730,420:Penelope",], + ["file:Menu:pull::0,1,36,21:file",file_cb, + ["load @L","save @S","save as","read @R","write @W", + "quit @Q"]], + ["line1:Line:::1,22,729,22:",], + ["line2:Line:::133,32,133,420:",], + ["line3:Line:::427,22,427,419:",], + ["list:Menu:pull::73,1,36,21:list",list_cb, + ["clear","reverse","delete range","sort", + ["by size","by bits","by notes"]]], + ["note:Menu:pull::145,1,36,21:note",note_cb, + ["edit @E","find @F"]], + ["symmetries:Label:::156,338,70,13:symmetries",], + ["tile:Menu:pull::37,1,36,21:tile",tile_cb, + ["next @N","previous @P","first","last","goto @G", + "delete @D","revert","copy @C","new","info @I"]], + ["transformations:Label:::8,32,105,13:transformations",], + ["view:Menu:pull::110,1,36,21:view",view_cb, + ["pattern","tile","tile zoom", + ["1:1","2:1","4:1","8:1"]]], + ["logo:Rect:invisible::26,373,32,32:",logo_cb], + ["symmet:Rect:grooved::155,363,74,42:",symmet_cb], + ["xform:Rect:grooved::26,57,58,256:",xform_cb], + ["grid:Rect:grooved::153,64,251,256:",grid_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/pextract.icn b/ipl/gprogs/pextract.icn new file mode 100644 index 0000000..60f96d7 --- /dev/null +++ b/ipl/gprogs/pextract.icn @@ -0,0 +1,101 @@ +############################################################################ +# +# File: pextract.icn +# +# Subject: Program to separate good and bad patterns +# +# Author: Ralph E. Griswold +# +# Date: September 1, 1993 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes the name of a file containing tile specifications +# on the command line. Tiles to be extracted are entered from standard +# input. Extracted tiles are written to standard output. +# +# Options: +# +# -b replace selected tiles by blank tiles +# -d delete selected tiles from specification file +# -c copy selected tiles, do not blank or delete +# them. This is the default; -c overrides +# -b and -d. +# +############################################################################ +# +# Links: options, patutils +# +############################################################################ + +link options +link patutils + +procedure main(args) + local file, input, i, hitlist, patlist, spec, lo, hi, output + local subspec, opts + + opts := options(args, "cbd") + if \opts["c"] then opts["b"] := opts["d"] := &null + if \opts["d"] then opts["b"] := 1 + + file := args[1] | stop("*** no pattern list specified") + + input := open(file) | stop(" *** cannot open input file") + + hitlist := set() # construct set of indices to remove + + while spec := read() do { + spec ? { + while subspec := tab(upto(',') | 0) do { + if insert(hitlist, integer(subspec)) then { # integer + move(1) | break + tab(many(' ')) + } + else { + subspec ? { + lo := tab(many(&digits)) & + ="-" & + hi := tab(many(&digits)) & + lo <= hi & + pos(0) | { + write(&errout, "*** bad specification") + next + } + if not(integer(hi) & integer(lo)) then { + write(&errout, "*** bad specification") + next + } + every insert(hitlist, 0 < (lo to hi)) + } + move(1) | break + tab(many(' ')) + } + } + } + } + + patlist := [] # read in list of patterns + + while put(patlist, readpatt(input)) + + close(input) + + output := open(file, "w") | + stop("*** cannot reopen specified file for output") + + every i := !sort(hitlist) do { # discard and "delete" + write(patlist[i]) | write(&errout, "*** ", i, " out of bounds") + if \opts["b"] then patlist[i] := "1,#0" + } + + if \opts["d"] then + every write(output, "1,#0" ~== !patlist) + else + every write(output, !patlist) + +end diff --git a/ipl/gprogs/pgmtoims.icn b/ipl/gprogs/pgmtoims.icn new file mode 100644 index 0000000..c58813d --- /dev/null +++ b/ipl/gprogs/pgmtoims.icn @@ -0,0 +1,111 @@ +############################################################################ +# +# File: pgmtoims.icn +# +# Subject: Program to make an image from a PGM file +# +# Author: Gregg M. Townsend +# +# Date: May 23, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: pgmtoims [-gn] [file] +# +# Pgmtoims reads a PGM rawbits file and writes an Icon image string. +# The "-gn" option (2 <= n <= 64) selects the palette; g41 is the +# default. +# +# Note that only rawbits-format PGM files can be read. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options +# +############################################################################ + +link options + +procedure main(args) + local opts, g, cs, f, w, h, maxv, data, s, i, n, ln + + # Process options. + opts := options(args, "g+") + g := \opts["g"] | 41 + if g < 2 | g > 64 | *args > 1 then + stop("usage: ", &progname, " [-gn] [file]") + + # Select the set of image characters according to the palette. + cs := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{}" + cs := cs[1+:g] + + # Open the file and read it into memory. + if *args = 1 then + f := open(args[1], "ru") | stop("can't open ", args[1]) + else + f := &input + s := "" + while s ||:= reads(f, 1000) + + # Crack the file header. + s ? { + ws() + if ="P" & any('1346') then + stop("input is not in PGM format; convert via \"ppmtopgm\"") + if ="P2" then + stop("input is not in *raw* PGM format") + if not ="P5" then + stop("input is not a PGM file") + ws() + w := tab(many(&digits)) # image width + ws() + h := tab(many(&digits)) # image height + ws() + maxv := tab(many(&digits)) # maximum byte value in input + tab(any(' \t\r\n')) + data := tab(0) # image data + } + + # Calculate the translation from input to output data bytes. + s := "" + every i := 0 to maxv do + s ||:= cs[1 + (g * i) / (maxv + 1)] + + # Figure out a reasonable line length for output, + # assuming not too many backslashes. + n := 79 > w / seq(1) + if w % n > 0 then + n +:= 1 + + # Translate the data a line at a time, and write. + map(data, &cset, s) ? { + write("\"", w, ",g", g, ",_") + while not pos(0) do wdata(move(w) | tab(0), n) + write("\"") + } +end + + +# wdata(s, n) -- write one line of data with max linelength n + +procedure wdata(s, n) + s ? while not pos(0) do + write(image(move(n) | tab(0)) [2:-1], "_") + return +end + + +# ws() -- skip whitespace. + +procedure ws() + while tab(many(' \t\r\n')) | (="#" & tab(upto('\n'))) + return +end diff --git a/ipl/gprogs/picktile.icn b/ipl/gprogs/picktile.icn new file mode 100644 index 0000000..ece5971 --- /dev/null +++ b/ipl/gprogs/picktile.icn @@ -0,0 +1,164 @@ +############################################################################ +# +# File: picktile.icn +# +# Subject: Program to pick a tile out of an image +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program provides an optionally magnified view of an image file. +# Clicking on a pixel produces a pattern specification for the tile +# with the selected upper-left corner. +# +# Options are: +# +# -z i zoom factor, default 1 (no magnification) +# -f use fixed size tiles rather than selection; default selection +# -w i width of tile, default 32 +# -h i height of tile, default width +# -I pick tiles to make icons; implies -z2, -f, -w38, -w38 (the +# larger size leaves room for error and trimming) +# -R i specs for ResEdit files; i = 32 or 16 +# -t trim whitepace around tile +# +# Typical usage is +# +# picktile image.xbm >image.tle +# +# The program terminates if "q" is pressed when in the image window. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, patxform, win, xcompat +# +############################################################################ + +link options +link patxform +link win +link xcompat + +procedure main(args) + local pixmap, wix, hix, c, x, y, event, opts, base, magnif, cols, rows + local arglist, state, x0, x1, y0, y1, pattern + + opts := options(args, "tz+w+h+If") + magnif := \opts["z"] | 1 + cols := \opts["w"] | 32 + rows := \opts["h"] | cols + + if \opts["I"] then { + magnif := 2 + cols := rows := 38 + opts["f"] := 1 + } + + pixmap := XBind(, , ,"image=" || args[1]) | + stop("*** cannot open image file") + + wix := WAttrib(pixmap, "width") + hix := WAttrib(pixmap, "height") + + win(magnif * wix, magnif * hix) + +# Build the magnified image. + +# But if the magnification happens to be 1, don't do it the dumb way. + + if magnif = 1 then + CopyArea(pixmap, &window) + + else { + every y := 0 to hix - 1 do { + arglist := [] + + every x := 0 to wix - 1 do { + c := Pixel(pixmap, x, y, 1, 1) + if c == "0,0,0" then { + every put(arglist, (magnif * x) | (magnif * y) | magnif | magnif) + } + x +:= 1 + } + + if *arglist > 0 then FillRectangle ! arglist + } + } + + if \opts["f"] then { # let user pick corners + while event := Event() do { + case event of { + "q": exit() + &lpress | &mpress | &rpress: { + pattern := pix2pat(pixmap, &x / magnif, &y / magnif, cols, rows) + if \opts["t"] then pattern := rows2pat(ptrim(pat2rows(pattern))) + write(pattern) + } + } + } + } + + + else { # let user drag to select area + state := "pick" # waiting for user to pick + + WAttrib("drawop=reverse") + WAttrib("linestyle=dashed") + + while event := Event() do { + if event === "q" then exit() + case state of { + "pick": { # pick the upper-left corner + if event === &lpress then { + x1 := x0 := &x # initial coordinates + y1 := y0 := &y + DrawRectangle(x0, y0, 0, 0) # start the selection rectangle + state := "select" # now select the rectangle + } + } + "select": { # select the rectangle + case event of { + &ldrag: { # searching ... + DrawRectangle(x0, y0, x1 - x0, y1 - y0) # erase rectangle + x1 := &x # new lower-right + y1 := &y + DrawRectangle(x0, y0, x1 - x0, y1 - y0) # new rectangle + } + &lrelease: { # got it! + DrawRectangle(x0, y0, x1 - x0, y1 - y0) # erase rectangle + x1 := &x # new lower-right + y1 := &y + DrawRectangle(x0, y0, x1 - x0, y1 - y0) # new rectangle + state := "decide" # now decide + } + } + } + "decide": { # is it wanted or not? + DrawRectangle(x0, y0, x1 - x0, y1 - y0) # erase rectangle + if event === &lpress then { + if (x0 <= &x <= x1) & (y0 <= &y <= y1) then { + pattern := pix2pat(pixmap, x0 / magnif, y0 / magnif, + (x1 - x0) / magnif, (y1 - y0) / magnif) + if \opts["t"] then + pattern := rows2pat(ptrim(pat2rows(pattern))) + write(pattern) + } + } + state := "pick" # go for another + } + } + } + } + +end diff --git a/ipl/gprogs/plat.icn b/ipl/gprogs/plat.icn new file mode 100644 index 0000000..881a9b2 --- /dev/null +++ b/ipl/gprogs/plat.icn @@ -0,0 +1,67 @@ +############################################################################ +# +# File: plat.icn +# +# Subject: Program to create image file with specified colors +# +# Author: Ralph E. Griswold +# +# Date: January 6, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces image files from color lists, in which the +# image file contains one pixel for each color. The image files are +# 16x16 pixels. If a color list has less than 256 colors, the rest +# of the image is black. If the color list has more than 256 colors +# only the first 256 are processed. +# +# The image file names have the basename of the color list files followed +# by _p and the suffix .gif. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, wopen +# +############################################################################ + +link basename +link wopen + +procedure main(args) + local line, file, name, input, i, j, color + + WOpen("canvas=hidden", "size=16,16", "bg=black") | + stop("*** cannot open window") + + every file := !args do { + input := open(file) | { + write(&errout, "*** cannot open ", file) + next + } + name := basename(file, ".clr") + EraseArea() + every i := 0 to 15 do + every j := 0 to 15 do { + color := read(input) | break + color ? { + Fg(tab(upto('\t') | 0)) | + write(&errout, "*** cannot set foreground") + } + DrawPoint(i, j) + } + WriteImage(name || "_p.gif") + close(input) + } + +end + + diff --git a/ipl/gprogs/plotter.icn b/ipl/gprogs/plotter.icn new file mode 100644 index 0000000..f13d337 --- /dev/null +++ b/ipl/gprogs/plotter.icn @@ -0,0 +1,199 @@ +############################################################################ +# +# File: plotter.icn +# +# Subject: Program to display planes of 3-space coordinates +# +# Author: Ralph E. Griswold +# +# Date: July 22, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program plots planes for coordinates in 3-space. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, io, ptutils, vsetup +# +############################################################################ + +link interact +link io +link ptutils +link vsetup + +global coords +global h_off +global half +global size +global pane +global plane +global root +global scale +global size +global v_off +global vidgets + +procedure main() + + vidgets := ui() + root := vidgets["root"] + + VSetItems(vidgets["coords"], filelist("*.crd")) + VSetState(vidgets["plane"], "xy") + + size := vidgets["pane"].uw + half := size / 2 + + pane := Clone("bg=white", "dx=" || (vidgets["pane"].ux + half), + "dy=" || (vidgets["pane"].uy + half)) + Clip(pane, -half, -half, size, size) + + EraseArea(pane, -half, -half, size, size) + + scale := 10 + h_off := 0 + v_off := 0 + + GetEvents(root, , shortcuts) + +end + +procedure offset_cb() + + repeat { + if TextDialog("Set offset:", ["horizontal", "vertical"], + [h_off, v_off], 5) == "Cancel" then fail + if h_off <- integer(dialog_value[1]) & + v_off <- integer(dialog_value[2]) then break + else { + Notice("Nonnumeric offset value.") + next + } + } + + return + +end + +procedure scale_cb() + + repeat { + if TextDialog("Set scale:", , scale, 5) == "Cancel" then fail + if scale := integer(dialog_value[1]) then break + else { + Notice("Nonnumeric scale value.") + next + } + } + + return + +end + +procedure file_cb(vidgets, value) + + case value[1] of { + "clear @C": clear_cb() + "plot @P": plot_cb() + "quit @Q": exit() + "snapshot @S": snapshot(pane, -half, -half, size, size) + } + + return + +end + +procedure coord_cb(vidget, value) + local input + + input := open(value) | { + Notice("Cannot open " || image(value) || ".") + fail + } + + coords := [] + + every put(coords, coord2pt(!input)) + + close(input) + + return + +end + +procedure plot_cb() + local p + + every p := !coords do { + case plane of { + "xy": DrawPoint(pane, scale * p.x + h_off, scale * p.y + v_off) + "yz": DrawPoint(pane, scale * p.y + h_off, scale * p.z + v_off) + "xz": DrawPoint(pane, scale * p.x + h_off, scale * p.z + v_off) + } + } + +end + +procedure plane_cb(vidget, value) + + plane := value + + return + +end + +procedure clear_cb() + + EraseArea(pane, -half, -half, size, size) + + return + +end + +procedure shortcuts(e) + + if &meta then case map(e) of { # fold case + "c": clear_cb() + "p": plot_cb() + "q": exit() + "s": snapshot(pane, -half, -half, size, size) + } + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=633,459", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,633,459:",], + ["clear:Button:regular::82,101,50,20:clear",clear_cb], + ["coords:List:w::13,198,190,244:",coord_cb], + ["file:Menu:pull::28,5,36,21:File",file_cb, + ["clear @C","plot @P","snapshot @S","quit @Q"]], + ["label1:Label:::28,45,35,13:plane",], + ["label2:Label:::50,174,105,13:coordinate file",], + ["line1:Line:::0,30,640,30:",], + ["offset:Button:regular::143,72,50,20:offset",offset_cb], + ["plane:Choice::3:25,68,43,63:",plane_cb, + ["xy","xz","yz"]], + ["plot:Button:regular::81,71,50,20:plot",plot_cb], + ["scale:Button:regular::144,101,50,20:scale",scale_cb], + ["pane:Rect:grooved::220,43,400,400:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/pme.icn b/ipl/gprogs/pme.icn new file mode 100644 index 0000000..6a29253 --- /dev/null +++ b/ipl/gprogs/pme.icn @@ -0,0 +1,180 @@ +############################################################################ +# +# File: pme.icn +# +# Subject: Program to edit pixmaps +# +# Author: Clinton L. Jeffery +# +# Date: April 30, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 2.0 +# +############################################################################ +# +# A (color) pixmap editor. +# +# Left, middle, and right buttons draw different colors. +# Press q or ESC to quit; press s to save. Capital "S" prompts for +# and saves under a new filename. +# Click on the little picture of the mouse to change one of the +# button's colors. Not very interesting on a monochrome server. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen, xcompat +# +############################################################################ + +link wopen +link xcompat +global w, WIDTH, HEIGHT, XBM, LMARGIN +global colors, colorbinds + +procedure main(argv) + local i, f, s, xpos, ypos, i8, j, j8, j8Plus, e, x, y + colors := [ "red", "green", "blue" ] + i := 1 + XBM := ".xpm" + WIDTH := 32 + HEIGHT := 32 + if *argv>0 & argv[1][1:5]=="-geo" then { + i +:= 1 + if *argv>1 then argv[2] ? { + WIDTH := integer(tab(many(&digits))) | stop("geo syntax") + ="x" | stop("geo syntax") + HEIGHT := integer(tab(0)) | stop("geo syntax") + i +:= 1 + } + } + LMARGIN := WIDTH + if LMARGIN < 65 then LMARGIN := 65 + if (*argv >= i) & + (f := open(s := (argv[i] | (argv[i]||(XBM|".xbm"))))) then { + close(f) + w := &window := WOpen("label=PixMap", "image="||s, "cursor=off") | + stop("cannot open window") + WIDTH <:= WAttrib(w, "width") + HEIGHT <:= WAttrib(w, "height") + LMARGIN := WIDTH + if LMARGIN < 65 then LMARGIN := 65 + pos := WAttrib("pos") + pos ? { + xpos := tab(many(&digits)) | stop(image(pos)) + ="," + ypos := tab(0) + } + WAttrib(w, "posx="||xpos, "posy="||ypos, + "width="||(WIDTH*8+LMARGIN+5), "height="||(HEIGHT*8)) + Event() + every i := 0 to HEIGHT-1 do { + i8 := i*8 + every j := 0 to WIDTH-1 do { + j8 := j*8 + j8Plus := j8 + LMARGIN + 5 + CopyArea(w, w, j, i, 1, 1, j8Plus, i8) + CopyArea(w, w, j, i, 1, 1, j8Plus+1, i8) + CopyArea(w, w, j8Plus, i8, 2, 1, j8Plus+2,i8) + CopyArea(w, w, j8Plus, i8, 4, 1, j8Plus+4, i8) + CopyArea(w, w, j8Plus, i8, 8, 1, j8Plus, i8+1) + CopyArea(w, w, j8Plus, i8, 8, 2, j8Plus, i8+2) + CopyArea(w, w, j8Plus, i8, 8, 4, j8Plus, i8+4) + } + } + } else { + w := &window := WOpen("label=PixMap", "cursor=off", + "width="||(LMARGIN+WIDTH*8+5), + "height="||(HEIGHT*8+5)) | + stop("cannot open window") + } + + colorbinds := [ XBind(w,"fg="||colors[1]), + XBind(w,"fg="||colors[2]), + XBind(w,"fg="||colors[3]) ] + every i := 1 to 3 do { + XDrawArc( 4+i*10, HEIGHT+68, 7, 22) + XFillArc(colorbinds[i], 5+i*10, HEIGHT+70, 5, 20) + } + DrawRectangle( 5, HEIGHT+55, 45, 60) + DrawRectangle( 25, HEIGHT+50, 5, 5) + DrawCurve(27, HEIGHT+50, + 27, HEIGHT+47, + 15, HEIGHT+39, + 40, HEIGHT+20, + 25, HEIGHT+5) + + Fg( "black") + every i := 0 to HEIGHT-1 do + every j := 0 to WIDTH-1 do + DrawRectangle( j*8+LMARGIN+5, i*8, 8, 8) + + DrawLine( 0, HEIGHT, WIDTH, HEIGHT, WIDTH, 0) + + repeat { + case e := Event(w) of { + "q"|"\e": return + "s"|"S": { + if /s | (e=="S") then s := getfilename() + write("saving image ", s, " with width ", image(WIDTH), + " height ", image(HEIGHT)) + WriteImage( s, 0, 0, WIDTH, HEIGHT) + } + &lpress | &ldrag | &mpress | &mdrag | &rpress | &rdrag : { + + x := (&x - LMARGIN - 5) / 8 + y := &y / 8 + + if (y < 0) | (y > HEIGHT-1) | (x > WIDTH) then next + if (x < 0) then { + if &x < 21 then getacolor(1, "left") + else if &x < 31 then getacolor(2, "middle") + else getacolor(3, "right") + until Event(w) === (&mrelease | &lrelease | &rrelease) + } + else dot(x, y, (-e-1)%3) + } + } + } +end + +procedure getacolor(n, s) + local wtmp, theColor + wtmp := WOpen("label=" || image(s||" button: "), "lines=1") | + stop("can't open temp window") + writes(wtmp,"[",colors[n],"] ") + theColor := read(wtmp) | stop("read fails") + close(wtmp) + wtmp := colorbinds[n] | stop("colorbinds[n] fails") + Fg(wtmp, theColor) | write("XFG(", theColor, ") fails") + XFillArc(wtmp, 5+n*10, HEIGHT+70, 5, 20) + colors[n] := theColor +end + +procedure dot(x, y, color) + if (x|y) < 0 then fail + FillRectangle(colorbinds[color+1], x*8+LMARGIN+5, y*8, 8, 8) + DrawPoint(colorbinds[color+1], x, y) + DrawRectangle( x*8+LMARGIN+5, y*8, 8, 8) +end + +procedure getfilename() + local s, pos, wprompt, rv + pos := "pos=" + every s := QueryPointer() do pos||:= (s-10)||"," + wprompt := WOpen("label=Enter a filename to save the pixmap", + "font=12x24", "lines=1", pos[1:-1]) | stop("can't xprompt") + rv := read(wprompt) + close(wprompt) + if not find(XBM, rv) then rv ||:= XBM + return rv +end diff --git a/ipl/gprogs/poller.icn b/ipl/gprogs/poller.icn new file mode 100644 index 0000000..ad69592 --- /dev/null +++ b/ipl/gprogs/poller.icn @@ -0,0 +1,80 @@ +############################################################################ +# +# File: poller.icn +# +# Subject: Program to record image as pixel coordinates +# +# Author: Ralph E. Griswold +# +# Date: December 30, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reads an image whose name is given on the command line and +# writes it out as an Icon list of pixels in the form of an include file. +# See the documentation below for details. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: lists, wopen +# +############################################################################ + +link lists +link wopen + +procedure main(args) + local colors, width, height, x, y, c + + WOpen("image=" || args[1]) | stop("*** cannot open image") + + colors := table() + + width := WAttrib("width") + height := WAttrib("height") + + x := y := 0 + + # Build table of argument lists for colors + + every c := Pixel() do { + x +:= 1 + if x % width = 0 then { + x := 0 + y +:= 1 + } + /colors[c] := [] # new color + put(colors[c], x, y) + } + + # Write Icon code for an include file. A list of argument lists + # is assigned to "pixels". Each argument list consists of the + # color followed by the pixel coordinates at which that color + # occurs + # + # The last element of the list is a three-element list giving the + # width, height, and number of colors in the image. Note that this + # is an easily accessible location and that it "solves" the problem + # that all previous lines are termianted by commas, so without it + # either there would be a trailing empty element in "pixels" + # or some painful code would be necessary to avoid it. + + write("pixels:=[") + + every c := key(colors) do { + push(colors[c], c) + write(limage(colors[c]), ",") + } + + write("[", width, ",", height, ",", *colors, "]") + write("]") + +end diff --git a/ipl/gprogs/procater.icn b/ipl/gprogs/procater.icn new file mode 100644 index 0000000..721389f --- /dev/null +++ b/ipl/gprogs/procater.icn @@ -0,0 +1,185 @@ +############################################################################ +# +# File: procater.icn +# +# Subject: Program to display concatenation sizes +# +# Author: Ralph E. Griswold +# +# Date: September 18, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays successive numbers by lines of corresponding +# height. When the display area is full, it scrolls from right to +# left. +# +# In this version, input is piped in. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, vsetup +# +############################################################################ + +link interact +link vsetup + +global vidgets +global root +global strip +global state +global gc_gray +global gc_black +global reset +global scale + +global width +global height + +procedure main(args) + + init(args) + + display() + +end + +procedure init(args) + + WOpen ! ui_atts() + + vidgets := ui() + + root := vidgets["root"] + + state := &null + scale := 1 + + width := vidgets["strip"].uw + height := vidgets["strip"].uh + + strip := Clone("dx=" || vidgets["strip"].ux, "dy=" || vidgets["strip"].uy) + Clip(strip, 0, 0, width, height) + gc_gray := Clone(strip, "fg=gray") + gc_black := Clone(strip, "fg=black") + +end + +procedure display() + local n, gc + + repeat { + repeat { + while (*Pending() > 0) | \state do + ProcessEvent(root, , shortcuts) + n := read() | { + Notice("End of data.") + fail + } + n ? { + if ="a" then { + n := tab(0) + gc := gc_gray + } + else gc := gc_black + } + n := scale * integer(n) | { + Notice("Nonnumeric data; terminating.") + break + } + n >:= height # Motif bug avoidance + CopyArea(strip, 1, 0, width - 1, height, 0, 0) + EraseArea(strip, width - 1, 0, width, height) + DrawLine(gc, width - 1, height - n, width - 1, height) + } + } + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "snapshot @S": return snapshot(strip, 0, 0, width, height) + "quit @Q": exit() + } + + fail + +end + +procedure configure_cb(vidget, value) + + case value[1] of { + "scale": { + repeat { + if TextDialog(, "scale", scale, 10) == "Okay" then { + scale := (0 < numeric(dialog_value[1])) | { + Notice("Invalid scale value.") + next + } + reset_cb() + return + } + else fail # user canceled + } + } + } + + fail + +end +procedure pause_cb(vidget, value) + + state := value + + return + +end + +procedure reset_cb() + + EraseArea(strip) + + return + +end + +procedure shortcuts(e) + + if &meta then + case map(e) of { + "q": exit() + "s": return snapshot(strip, 0, 0, width, height) + } + else fail + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=477,255", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,477,255:",], + ["configure:Menu:pull::36,0,71,21:Configure",configure_cb, + ["scale"]], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["open @O","snapshot @S","quit @q"]], + ["line1:Line:::0,22,477,22:",], + ["pause:Button:regular:1:11,43,42,20:pause",pause_cb], + ["reset:Button:regular::11,76,42,20:reset",reset_cb], + ["strip:Rect:grooved::63,37,400,200:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/profile.icn b/ipl/gprogs/profile.icn new file mode 100644 index 0000000..8b89a2d --- /dev/null +++ b/ipl/gprogs/profile.icn @@ -0,0 +1,305 @@ +############################################################################ +# +# File: profile.icn +# +# Subject: Program to display scrolling histogram +# +# Author: Ralph E. Griswold +# +# Date: January 21, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays successive numbers by lines of corresponding +# height. When the display area is full, it scrolls from right to +# left. +# +# If a line has a number followed by a blank and a string, the string +# is interpreted as a color. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, navitrix, vsetup +# +############################################################################ + +link interact +link vsetup + +global animate # animation toggle +global count # frame count +global height # height of scrolling area +global input # input file +global name # input file name +global offset # base-line offset +global pause # pause vidget +global prefix # image file name prefix +global rate # sample rate +global reset # reset switch +global scale # vertical scale +global state # pause/run state +global strip # graphics context for display +global width # width of scrolling area +global vidgets +global root + +procedure main() + local value, n, color + + init() + + color := "black" # default color + + while value := read() do { + if (*Pending() > 0) | \state then + ProcessEvent(root, , shortcuts) + value ? { + n := tab(upto(' \t') | 0) + if tab(many(' \t')) then color := tab(0) + } + n := (scale * numeric(n)) | { + Fg("black") + Notice("Nonnumeric data; terminating.") + exit() + } + n >:= height # clip to avoid window-manager bugs + CopyArea(strip, 1, 0, width - 1, height, 0, 0) + EraseArea(strip, width - 1, 0, width, height) + Fg(strip, color) | stop("bad color: ", image(color)) + DrawLine(strip, width - 1, height - n - offset, width - 1, + height - offset) + if \animate then + WriteImage(strip, prefix || right(count +:= 1, 4, "0") || ".gif", + 0, 0, width, height) + } + + Fg("black") + + case TextDialog("End of stream.", , , , ["Quit", "Snapshot", "Hold"]) of { + "Quit" : exit() + "Snapshot" : snapshot(strip, 0, 0, width, height) + "Hold" : WDone() + } + +end + +procedure init() + + vidgets := ui() + + root := vidgets["root"] + pause := vidgets["pause"] + VSetState(pause, 1) # initially paused + + name := "" + rate := 1 + scale := 1 + offset := 0 + + count := 0 + prefix := "image" + + width := vidgets["strip"].uw + height := vidgets["strip"].uh + + strip := Clone("dx=" || vidgets["strip"].ux, "dy=" || vidgets["strip"].uy) + Clip(strip, 0, 0, width, height) + + return + +end + +procedure animation_cb(vidget, value) + + case value[1] of { + "prefix" : set_prefix() + "rate" : set_frame_rate() + } + +end + +procedure set_prefix() + + return + +end + +procedure set_frame_rate() + + return + +end + +procedure animate_cb(vidget, value) + + animate := value + + return + +end + +procedure parameters_cb(vidget, value) + + case value[1] of { + "scale @V" : set_scale() + "offset @F" : set_offset() + "rate @R" : set_rate() + } + + fail + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "snapshot @S" : return snapshot(strip, 0, 0, width, height) + "quit @Q" : exit() + } + +end + +procedure pause_cb(vidget, value) + + state := value + + return + +end + +procedure clear_cb() + + EraseArea(strip) + + return + +end + +procedure set_rate() + + repeat { + if TextDialog(, "sample rate", rate, 10) == "Okay" then { + rate := (0 < numeric(dialog_value[1])) | { + Notice("Invalid sample rate.") + next + } + clear_cb() + return + } + else fail # user canceled + } + +end + +procedure set_offset() + + repeat { + if TextDialog(, "vertical offset", offset, 10) == "Okay" then { + offset := numeric(dialog_value[1]) | { + Notice("Invalid offset.") + next + } + clear_cb() + return + } + else fail # user canceled + } + +end + +procedure set_scale() + + repeat { + if TextDialog(, "vertical scale", scale, 10) == "Okay" then { + scale := (0 < numeric(dialog_value[1])) | { + Notice("Invalid scale value.") + next + } + clear_cb() + return + } + else fail # user canceled + } + +end + +procedure shortcuts(e) + + if &meta then + case map(e) of { + "c" : clear_cb() + "f" : set_offset() + "p" : if \state then VSetState(pause) else VSetState(pause, 1) + "q" : exit() + "r" : set_rate() + "s" : snapshot(strip, 0, 0, width, height) + "v" : set_scale() + } + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=651,305", "bg=pale gray", "label=Scrolling Histogram"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,651,305:Scrolling Histogram",], + ["animate:Button:regular:1:21,189,56,20:movie",animate_cb], + ["animation:Menu:pull::113,1,71,21:Animation",animation_cb, + ["prefix","rate","clear"]], + ["clear:Button:regular::21,88,56,20:clear",clear_cb], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["snapshot @S","quit @q"]], + ["label1:Label:::619,144,21,13:100",], + ["label10:Label:::90,269,21,13:500",], + ["label11:Label:::584,269,21,13: 0",], + ["label2:Label:::619,195,21,13: 50",], + ["label3:Label:::619,94,21,13:150",], + ["label4:Label:::619,45,21,13:200",], + ["label5:Label:::619,247,21,13: 0",], + ["label6:Label:::489,269,21,13:100",], + ["label7:Label:::388,269,21,13:200",], + ["label8:Label:::287,269,21,13:300",], + ["label9:Label:::188,269,21,13:400",], + ["line10:Line:::501,253,501,262:",], + ["line11:Line:::200,255,200,264:",], + ["line12:Line:::500,40,500,49:",], + ["line13:Line:::200,40,200,49:",], + ["line14:Line:::615,51,604,51:",], + ["line15:Line:::615,253,604,253:",], + ["line16:Line:::603,256,603,265:",], + ["line17:Line:::101,255,101,264:",], + ["line18:Line:::101,253,90,253:",], + ["line19:Line:::100,51,89,51:",], + ["line2:Line:::90,151,99,151:",], + ["line20:Line:::603,40,603,49:",], + ["line21:Line:::101,40,101,49:",], + ["line22:Line:::400,255,400,264:",], + ["line23:Line:::400,40,400,49:",], + ["line3:Line:::90,200,99,200:",], + ["line4:Line:::90,100,99,100:",], + ["line5:Line:::615,100,604,100:",], + ["line6:Line:::615,151,604,151:",], + ["line7:Line:::615,201,604,201:",], + ["line8:Line:::300,255,300,264:",], + ["line9:Line:::300,40,300,49:",], + ["menu line:Line:::0,23,655,23:",], + ["parameters:Menu:pull::35,1,78,21:Parameters",parameters_cb, + ["scale @V","offset @F","rate @R"]], + ["pause:Button:regular:1:21,41,56,20:pause",pause_cb], + ["strip:Rect:grooved::100,50,504,204:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/profiler.icn b/ipl/gprogs/profiler.icn new file mode 100644 index 0000000..666a154 --- /dev/null +++ b/ipl/gprogs/profiler.icn @@ -0,0 +1,206 @@ +############################################################################ +# +# File: profiler.icn +# +# Subject: Program to display number magnitudes +# +# Author: Ralph E. Griswold +# +# Date: November 21, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays successive numbers by lines of corresponding +# height. When the display area is full, it scrolls from right to +# left. +# +# If the -p option is given, data is taken from standard input; this +# is useful when input is piped into the program. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, vsetup +# +############################################################################ + +link interact +link vsetup + +global vidgets +global root +global strip +global state +global reset +global input +global scale +global fnc + +global width +global height + +procedure main(args) + + init(args) + + display() + +end + +procedure init(args) + + vidgets := ui() + + root := vidgets["root"] + + state := &null + scale := 1 + fnc := 1 # linear scaling :-) + if args[1] == "-p" then input := &input + + width := vidgets["strip"].uw + height := vidgets["strip"].uh + + strip := Clone("dx=" || vidgets["strip"].ux, "dy=" || vidgets["strip"].uy) + Clip(strip, 0, 0, width, height) + +end + +procedure display() + local n + + repeat { + repeat { + while (*Pending() > 0) | \state | /input do + ProcessEvent(root, , shortcuts) + n := read(input) | { + Notice("End of data") + break + } + n := integer(fnc(n * scale)) | { + Notice("Nonnumeric data; terminating input") + break + } + n >:= height # Motif bug avoidance + EraseArea(strip, width - 1, 0, width - 1, height) + DrawLine(strip, width - 1, height - n, width - 1, height) + CopyArea(strip, 1, 0, width - 1, height, 0, 0) + } + input := &null + } + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "open @O": load() + "snapshot @S": return snapshot(strip) + "quit @Q": exit() + } + + fail + +end + +procedure configure_cb(vidget, value) + + case value[1] of { + "scale": { + repeat { + if TextDialog(, "scale", scale, 10) == "Okay" then { + scale := (0 < numeric(dialog_value[1])) | { + Notice("Invalid scale value") + next + } + reset_cb() + return + } + else fail # user canceled + } + } + "function": { + repeat { + if TextDialog(, "function", fnc, 10) == "Okay" then { + (proc | numeric)(fnc <-dialog_value[1]) | { + Notice("Invalid function specification") + next + } + reset_cb() + return + } + else fail # user canceled + } + } + } + + fail + +end +procedure pause_cb(vidget, value) + + state := value + + return + +end + +procedure reset_cb() + + EraseArea(strip) + + return + +end + +procedure shortcuts(e) + + if &meta then + case map(e) of { + "o": load() + "q": exit() + "s": return snapshot(strip) + } + else fail + +end + +procedure load() + + if load_file() == "Okay" then { + input := dialog_value + reset_cb() + return + } + else fail + +end + + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=477,255", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,477,255:",], + ["configure:Menu:pull::36,0,71,21:Configure",configure_cb, + ["scale","function"]], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["open @O","snapshot @S","quit @q"]], + ["line1:Line:::0,22,477,22:",], + ["pause:Button:regular:1:11,43,42,20:pause",pause_cb], + ["reset:Button:regular::11,76,42,20:reset",reset_cb], + ["strip:Rect:grooved::63,37,400,200:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/prompt.icn b/ipl/gprogs/prompt.icn new file mode 100644 index 0000000..4450271 --- /dev/null +++ b/ipl/gprogs/prompt.icn @@ -0,0 +1,44 @@ +############################################################################ +# +# File: prompt.icn +# +# Subject: Program to prompt in a window +# +# Author: Clinton L. Jeffery +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# A utility for interactive shell scripts. Called from a +# shell script, it pops up a window, writes its arguments out as +# a prompt, and echos the user's response to standard output where +# the shell script can use it (by means of the backquote character). +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(args) + local s2, w + + pos := "pos=" + every s2 := QueryPointer() do pos ||:= (s2-10) || "," + + w := WOpen("label=prompt", "cursor=on", "font="||("12x24"|"fixed"), + "lines=1", pos[1:-1]) | stop("opening the window fails") + every writes(w,!args," ") + write(read(w)) +end diff --git a/ipl/gprogs/randweav.icn b/ipl/gprogs/randweav.icn new file mode 100644 index 0000000..b6f0463 --- /dev/null +++ b/ipl/gprogs/randweav.icn @@ -0,0 +1,254 @@ +############################################################################ +# +# File: randweav.icn +# +# Subject: Program to create random weavable patterns +# +# Author: Gregg M. Townsend +# +# Date: April 6, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Randweav is an interactive program for generating random +# weavable patterns. The top and left rows of the displayed +# pattern are a "key" to the vertical and horizontal threads +# of an imaginary loom. The colors of the other cells are chosen +# so that each matches either the vertical or horizontal thread +# with which it is aligned. +# +# The interactive controls are as follows: +# +# Colors Specifies the number of different colors from which +# the threads are selected. +# +# If "cycle warp" is checked, the vertical thread colors +# repeat regularly. If "cycle weft" is checked, the +# horizontal thread colors repeat regularly. +# +# RENDER When pressed, generates a new random pattern. +# Pressing the Enter key or space bar does the same thing. +# +# Side Specifies the number of threads along each side +# of the pattern. The pattern is always square. +# +# Bias Specifies as a percentage the probability that the +# vertical thread will determine the color of a pixel. +# +# If "perfect" is checked, vertical and horizontal +# threads alternate perfectly, ignoring the bias value. +# +# Save Brings up a dialog for saving the pattern as an image. +# +# Quit Exits the program. +# +# Note that the mouse must be over a numeric field to type in +# a new value. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: random, vsetup +# +############################################################################ + +link random +link vsetup + + +global vidgets # table of vidgets +global root # root vidget +global region # pattern region + +global hidwin # hidden window for saving to file + +global allcolors # string of all palette colors + +global maxsiz # maximum pattern size +global patsize # pattern size selected + +$define PALETTE "c1" # color palette +$define PREFCOLORS "06NBCDFsHIJM?!" # preferred colors + + +procedure main(args) + + randomize() + allcolors := PREFCOLORS || (PaletteChars(PALETTE) -- PREFCOLORS) + + Window ! put(ui_atts(), args) # open window + vidgets := ui() # set up vidgets + root := vidgets["root"] + region := vidgets["region"] + VSetState(vidgets["vcyclic"], 1) # default "cycle warp" on + VSetState(vidgets["hcyclic"], 1) # default "cycle weft" on + + hidwin := WOpen("canvas=hidden", # open hidden window + "width=" || region.uw, "height=" || region.uh) + + maxsiz := region.uw # set maximum size + maxsiz >:= region.uh + + render() # draw once without prompting + GetEvents(root, , all) # then wait for events +end + + +# all(a, x, y) -- process all events, checking for keyboard shortcuts + +procedure all(a, x, y) + if a === !" \n\r" then render() # draw new pattern for SPACE, CR, LF + else if &meta then case a of { + !"qQ": exit() # exit for @Q + !"sS": save() # save image for @S + } + return +end + + +# render() -- draw a new pattern according to current parameters + +procedure render() + local ncolors, bias + local s, x, y, w, h, z, k + static prevsize + + ncolors := txtval("colors", 1, *allcolors) # retrieve "Colors" setting + patsize := txtval("side", 1, maxsiz) # retrieve "Side" setting + bias := txtval("bias", 0, 100) # retrieve "Bias" setting + + k := (shuffle(PREFCOLORS) | allcolors)[1+:ncolors] # pick a color set + s := genpatt(patsize, k, bias / 100.0) # generate a pattern + DrawImage(hidwin, 0, 0, s) # draw on hidden win + + z := maxsiz / patsize # calculate scaling + x := region.ux + (region.uw - z * patsize) / 2 + y := region.uy + (region.uh - z * patsize) / 2 + + # copy to main window with enlargement + if prevsize ~===:= patsize then + EraseArea(region.ux, region.uy, region.uw, region.uh) # erase old pattern + Zoom(hidwin, &window, 0, 0, patsize, patsize, x, y, z * patsize, z * patsize) + + return +end + + +# genpatt(size, colors, bias) -- generate a new pattern as DrawImage() string + +procedure genpatt(size, colors, bias) + local warp, weft, perfect, s, x, y, w + + # choose thread colors + warp := genthreads(size, colors, VGetState(vidgets["vcyclic"])) + weft := genthreads(size, colors, VGetState(vidgets["hcyclic"])) + + # initialize output string (including first row) + s := size || "," || PALETTE || "," || warp + + perfect := VGetState(vidgets["perfect"]) + + # fill in remaining rows + every y := 2 to size do { + w := ?weft[y] # get weft color + s ||:= w # put in first column + if \perfect then + every x := 2 to size do # fill the rest (perfect case) + s ||:= if ((x + y) % 2) = 0 then w else warp[x] + else + every x := 2 to size do # fill the rest (random case) + s ||:= if ?0 > bias then w else warp[x] + } + + return s +end + + +# genthreads(n, colors, cyclic) -- generate a set of warp or weft threads + +procedure genthreads(n, colors, cyclic) + local s + + if \cyclic then + return repl(shuffle(colors), 1 + n / *colors)[1+:n] + + s := "" + every 1 to n do s ||:= ?colors + return s +end + + + +# txtval(s, min, max) -- get numeric value from named vidget and clamp to range + +procedure txtval(s, min, max) + local v, n + + v := vidgets[s] # find the vidget + VEvent(v, "\r", v.ax, v.ay) # set RETURN event to update state + n := integer(VGetState(v)) | min # retrieve int value, else use minimum + n <:= min # limit value by min and max + n >:= max + VSetState(v, n) # update vidget with validated value + return n # return value +end + + +# save() -- present dialog box and save pattern as image file + +procedure save() + local g + + g := WAttrib("gamma") # save old gamma value + WAttrib("gamma=1.0") # don't gamma-correct on write + repeat case OpenDialog("Save pattern as:") of { + "Cancel": { + WAttrib("gamma=" || g) + fail + } + "Okay": { + if WriteImage(hidwin, dialog_value, 0, 0, patsize, patsize) then + break + else + Notice("cannot write file:", dialog_value) + } + } + WAttrib("gamma=" || g) # restore gamma value + return +end + + +procedure quit() + exit() +end + + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=380,492", "bg=pale gray", "label=weaver"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,380,492:weaver",], + ["bias:Text::3:285,37,87,19:Bias: \\=60",], + ["colors:Text::3:10,9,87,19:Colors: \\=6",], + ["hcyclic:Button:checkno:1:5,56,97,20:cycle weft",], + ["perfect:Button:checkno:1:281,57,76,20:perfect",], + ["quit:Button:regular::293,462,78,20:quit @Q",quit], + ["render:Button:regular::159,24,72,36:RENDER",render], + ["save:Button:regular::8,462,78,20:save @S",save], + ["side:Text::3:285,8,87,19:Side: \\=90",], + ["vcyclic:Button:checkno:1:5,36,97,17:cycle warp",], + ["outline:Rect:sunken::153,18,84,48:",], + ["region:Rect:grooved::8,84,364,364:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/randweb.icn b/ipl/gprogs/randweb.icn new file mode 100644 index 0000000..efc6683 --- /dev/null +++ b/ipl/gprogs/randweb.icn @@ -0,0 +1,59 @@ +############################################################################ +# +# File: randweb.icn +# +# Subject: Program to draw random web design +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program connects lines in all possible ways between i randomly +# selected points in a window. The value of i is given on the command +# line (default 20). Large values of i produce unattractively dense +# structures. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: gobject, joinpair, random, wopen +# +############################################################################ + +link gobject +link joinpair +link random +link wopen + +procedure main(argl) + local i, j, k, angle, incr, points, size, radius + + i := integer(argl[1]) | 20 + + size := 300 + radius := size / 2 + + WOpen("label=random web", "width=" || size, "height=" || size) | + stop("*** cannot open window") + + points := [] + + randomize() + + every j := 1 to i do + put(points, Point(?size, ?size)) + + joinpair(points, points) + + Event() + +end diff --git a/ipl/gprogs/recticle.icn b/ipl/gprogs/recticle.icn new file mode 100644 index 0000000..e71d491 --- /dev/null +++ b/ipl/gprogs/recticle.icn @@ -0,0 +1,118 @@ +############################################################################ +# +# File: recticle.icn +# +# Subject: Program to draw rectangles recursively +# +# Authors: Gregg M. Townsend and Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws filled color rectangles recursively. +# +# The options supported are +# +# -w i width of image; default 400 +# -h i height of image; default 250 +# -p s palette; default "c3" +# -g i gap between rectangles; default 3 +# -i save image file; default no +# -n s default image file prefix; default "recticle" +# -m i minimum length of side; default 10 +# -b i bias -- affects size choices; default 20 +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: colrlist, options, random, wopen +# +############################################################################ + +link colrlist +link options +link random +link wopen + +global bias +global gap +global minside +global palette + +procedure main(args) + local w, h, opts, name + + opts := options(args, "w+h+p:g+b+m+n:i") + + w := \opts["w"] | 400 + h := \opts["h"] | 250 + palette := \opts["p"] | "c3" + PaletteChars(palette) | stop("*** invalid palette: ", palette) + gap := \opts["g"] | 3 + bias := \opts["b"] | 20 + name := \opts["n"] | "recticle" + minside := \opts["m"] | 10 + + WOpen("width=" || w, "height=" || h, "canvas=hidden") | + stop("*** cannot open window") + + randomize() + + rect(gap, gap, w - gap, h - gap) + + if \opts["i"] then WriteImage(name || ".gif") + + WAttrib("canvas=normal") + + WDone() + +end + +# rect(x,y,w,h) -- draw rectangle, possibly subdivided, at (x,y) + +procedure rect(x, y, w, h) + local d + static colors + + initial colors := colrplte(palette) + + if d := divide(w < h) then { # if cut horizontally: + rect(x, y, w, d) # draw top portion + rect(x, y + d, w, h - d) # draw bottom portion + } + else if d := divide(w) then { # if cut vertically: + rect(x, y, d, h) # draw left portion + rect(x + d, y, w - d, h) # draw right portion + } + else { # else draw single rect + Fg(?colors) # set random color + FillRectangle(x, y, w - gap, h - gap) # draw + } + + return + +end + + +# divide(n) -- find division point along length n +# +# Choose and return a division point at least minside units from +# either end. Fail if the length is too small to subdivide; +# also fail randomly, depending partially on the bias setting. + +procedure divide(n) + + if (n > 2 * minside) & (?n > bias) then + return minside + ?(n - 2 * minside) + else + fail + +end diff --git a/ipl/gprogs/rectile.icn b/ipl/gprogs/rectile.icn new file mode 100644 index 0000000..c767ee9 --- /dev/null +++ b/ipl/gprogs/rectile.icn @@ -0,0 +1,63 @@ +############################################################################ +# +# File: rectile.icn +# +# Subject: Program to extract portion of image +# +# Author: Ralph E. Griswold +# +# Date: August 26, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program extracts a fixed rectangle from the images given on the +# command line. +# +# The supported options are: +# +# -x i x coordinate of upper-left corner of rectangle; default 0 +# -y i y coordinate of upper-left corner of rectangle; default 0 +# -w i width of rectangle; default 64 +# -h i height of rectangle; default 64 +# -p s prefix for name of saved file; default "rect_"; may be +# "", in which case the input file is overridden. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, wopen +# +############################################################################ + +link options +link wopen + +procedure main(args) + local opts, prefix, x, y, w, h, win + + opts := options(args, "x+y+w+h+p:") + + x := \opts["x"] | 0 + y := \opts["y"] | 0 + w := \opts["w"] | 64 + h := \opts["h"] | 64 + + prefix := \opts["p"] | "rect_" + + every name := !args do { + win := WOpen("canvas=hidden", "image=" || name) | { + write(&errout, "*** cannot open ", name) + next + } + WriteImage(win, prefix || name, x, y, w, h) | + write(&errout, "*** cannot write rectangle for ", name) + WClose(win) + } +end diff --git a/ipl/gprogs/rects.icn b/ipl/gprogs/rects.icn new file mode 100644 index 0000000..c88b180 --- /dev/null +++ b/ipl/gprogs/rects.icn @@ -0,0 +1,106 @@ +############################################################################ +# +# File: rects.icn +# +# Subject: Program to tile window with colored rectangles +# +# Author: Gregg M. Townsend +# +# Date: December 3, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Rects tiles the window with randomly colored nonuniform +# rectangles. Pressing the space bar produces a new tiling. +# Pressing "q" exits the program. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, random +# +############################################################################ + + + +link graphics +link random + +$define MinSide 10 # minimum size of a rectangle side +$define Gap 3 # gap between rectangles +$define Bias 20 # bias setting -- affects size choices + + + +procedure main(args) + local w, h + + Window("bg=white", "width=600", "height=400", args) + w := integer(WAttrib("width")) + h := integer(WAttrib("height")) + + randomize() + rect(Gap, Gap, w - Gap, h - Gap) + + repeat case Event() of { + "q": exit() + " ": { + EraseArea() + rect(Gap, Gap, w - Gap, h - Gap) + } + } + +end + + + +# rect(x,y,w,h) -- draw rectangle, possibly subdivided, at (x,y) + +procedure rect(x, y, w, h) + local d + static darkness, hue + initial { + darkness := ["light", "medium", "dark", "deep"] + hue := ["red", "orange", "yellow", "green", "blue", "gray"] + } + + if d := divide(w < h) then { # if cut horizontally: + rect(x, y, w, d) # draw top portion + rect(x, y + d, w, h - d) # draw bottom portion + } + else if d := divide(w) then { # if cut vertically: + rect(x, y, d, h) # draw left portion + rect(x + d, y, w - d, h) # draw right portion + } + else { # else draw single rect + Fg(?darkness || " strong " || ?hue) # set random color + FillRectangle(x, y, w - Gap, h - Gap) # draw + } + + return + +end + + + +# divide(n) -- find division point along length n +# +# Choose and return a division point at least MinSide units from +# either end. Fail if the length is too small to subdivide; +# also fail randomly, depending partially on the Bias setting. + +procedure divide(n) + + if (n > 2 * MinSide) & (?n > Bias) then + return MinSide + ?(n - 2 * MinSide) + else + fail + +end diff --git a/ipl/gprogs/repeater.icn b/ipl/gprogs/repeater.icn new file mode 100644 index 0000000..707b243 --- /dev/null +++ b/ipl/gprogs/repeater.icn @@ -0,0 +1,92 @@ +############################################################################ +# +# File: repeater.icn +# +# Subject: Program to repeat image +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program repeats images a specified number of times. The image +# names are given on the command line. +# +# The supported options are: +# +# -h i repeat horizontally i times, default 1. +# -v i repeat vertically i times, default 1. +# -a i repeat i times perpendicular to smallest dimension; +# default 10; and 1 time perpendicular to the largest dimension; +# overrides -h and 0v. +# -l i limit size in repeat direction to i; default 256; only applies +# if -a is in force. +# -p s prefix to prepend to image name, default "rep_". Can +# be empty string, in which case the input image is +# overwritten. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, tile, wopen +# +############################################################################ + +link options +link tile +link wopen + +procedure main(args) + local opts, prefix, h_rep, v_rep, win1, win2, name, width, height + local auto, wdim, hdim, limit + + opts := options(args, "h+v+a+l+p:") + + h_rep := \opts["h"] | 1 + v_rep := \opts["v"] | 1 + prefix := \opts["p"] | "rep_" + auto := \opts["a"] + limit := \opts["l"] | 256 + + every name := !args do { + win1 := WOpen("canvas=hidden", "image=" || name) | { + write(&errout, "*** cannot open ", name) + next + } + width := WAttrib(win1, "width") + height := WAttrib(win1, "height") + if \auto then { + if width > height then { + hdim := height * auto + hdim >:= limit + wdim := width + } + else { + hdim := height + wdim := width * auto + wdim >:= limit + } + } + else { + hdim := height * h_rep + wdim := width * v_rep + } + win2 := WOpen("canvas=hidden", "width=" || wdim, "height=" || hdim) | { + write(&errout, "*** cannot open window for repeat") + WClose(win1) + next + } + tile(win1, win2) + WriteImage(win2, prefix || name) + WClose(win1) + WClose(win2) + } +end diff --git a/ipl/gprogs/rings.icn b/ipl/gprogs/rings.icn new file mode 100644 index 0000000..61739b5 --- /dev/null +++ b/ipl/gprogs/rings.icn @@ -0,0 +1,108 @@ +############################################################################ +# +# File: rings.icn +# +# Subject: Program to draw tiles of rings and circles +# +# Author: Gregg M. Townsend +# +# Date: July 13, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces seamless tiles with drawings of circles and +# rings. +# +# It words from characters input to the window: +# +# q quit +# c draw 10 random circles +# r draw 5 random rings +# W writes image to GIF file; files are named ring000.gif, +# ring001.gif, ... +# E erases the window +# F fills the window +# R reverses the colors +# +# At present there are no options except those provided for +# opening the window. +# +# Some modifications have been made by Ralph E. Griswold +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: xio, xutils +# +############################################################################ + +$define W 128 +$define H 128 + +link xio, xutils + +procedure main(args) + local count + + count := -1 + Window(args) + repeat case Event() of { + QuitEvents(): exit() + "c": randrop(circle, 10) + "r": randrop(ring, 5) + "W": WriteImage("rings" || right(count +:= 1, 3, "0") || ".gif", + , , W, W) + "E": EraseArea() + "F": FillRectangle() + "R": {WAttrib("drawop=reverse"); FillRectangle(); WAttrib("drawop=copy")} + } +end + +procedure replicate() + CopyArea(0, 0, W, H, 0, H) + CopyArea(0, 0, W, 2 * H, W, 0) + CopyArea(0, 0, 2 * W, 2 * H, 2 * W, 0) + CopyArea(0, 0, 4 * W, 2 * H, 0, 2 * H) + DrawLine(W, 0, W, H, 0, H) + return +end + +procedure randrop(p, n) + local x, y + every 1 to n do { + x := ?W - W / 2 + y := ?H - H / 2 + p(x, y) + p(x, y + H) + p(x + W, y) + p(x + W, y + W) + } + replicate() + return +end + +procedure ring(x, y) + static outer, inner + initial { + outer := Clone("fg=black", "linewidth=5") + inner := Clone("fg=white", "linewidth=3") + } + DrawCircle(outer, x, y, 30) + DrawCircle(inner, x, y, 30) + return +end + +procedure circle(x, y) + static white + initial white := Clone("fg=white") + FillCircle(white, x, y, 12) + DrawCircle(x, y, 12) # change to 10 for gaps + return +end diff --git a/ipl/gprogs/rolypoly.icn b/ipl/gprogs/rolypoly.icn new file mode 100644 index 0000000..2c2011f --- /dev/null +++ b/ipl/gprogs/rolypoly.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: rolypoly.icn +# +# Subject: Program to draw ``abstract'' art +# +# Author: Ralph E. Griswold +# +# Date: September 28, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program makes a simple random abstract sketch. It supports these +# options: +# +# -p i number of points (default 10) +# -s i size of (square) window (default 300) +# -r randomize seed +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: random, options, gobject, randfigs, wopen +# +############################################################################ + +link random +link options +link gobject +link randfigs +link wopen + +procedure main(argl) + local opts, n, size, points, p + + opts := options(argl, "p+s+r") + + n := \opts["p"] | 10 + size := \opts["s"] | 300 + if \opts["r"] then randomize() + + WOpen("label=rolypoly", "size=" || size || "," || size) | + stop("*** cannot open window") + + points := [] # list of x,y coordinates + + every p := random_points(size, size) \ n do + every put(points, \!p) # z coordinate is null + + # here's the fun + every (FillPolygon | DrawCurve) ! points + + Event() # hold window open for an event + +end diff --git a/ipl/gprogs/rows2blp.icn b/ipl/gprogs/rows2blp.icn new file mode 100644 index 0000000..8dbe922 --- /dev/null +++ b/ipl/gprogs/rows2blp.icn @@ -0,0 +1,40 @@ +############################################################################ +# +# File: rows2blp.icn +# +# Subject: Program to convert row file to bi-level pattern +# +# Author: Ralph E. Griswold +# +# Date: October 30, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: patutils +# +############################################################################ + +link patutils + +procedure main() + local rows + + rows := [] + + while put(rows, read()) + + write(rows2pat(rows)) + +end diff --git a/ipl/gprogs/rows2isd.icn b/ipl/gprogs/rows2isd.icn new file mode 100644 index 0000000..4d1b3fb --- /dev/null +++ b/ipl/gprogs/rows2isd.icn @@ -0,0 +1,106 @@ +############################################################################ +# +# File: rows2isd.icn +# +# Subject: Program to produce a ISD from bi-level pattern +# +# Author: Ralph E. Griswold +# +# Date: November 16, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes a row file or BLP from standard input +# and writes an ISD for a draft to standard output. +# +############################################################################ +# +# Links: weavutil, xcode, patutils, patxform +# +############################################################################ + +link patutils +link patxform +link weavutil +link xcode + +procedure main(args) + local rows, cols, treadling, threading, count, tieup, y, width, height + local shafts, treadles, i, tie_line, row, treadle, draft, p, line + + line := read() | stop("empty file") + + if upto("#", line) then rows := pat2rows(line) + else { + rows := [line] + while put(rows, read()) # read in row pattern + } + + cols := protate(rows) # rotate to get columns + + treadles := examine(rows) # get treadles + shafts := examine(cols) # get shafts + + treadling := [] # construct treadling sequence + every put(treadling, treadles[!rows]) + + threading := [] # construct threading sequence + every put(threading, shafts[!cols]) + + tieup := [] + + every row := key(treadles) do { # get unique rows + treadle := treadles[row] # assigned treadle number + tie_line := repl("0", *shafts) # blank tie-up line + every i := 1 to *row do # go through row + if row[i] == "1" then # if warp on top + tie_line[threading[i]] := "1" # mark shaft position + put(tieup, tie_line) # add line to tie-up + } + + draft := isd("rows2isd") + + draft.threading := threading + draft.treadling := treadling + draft.shafts := *shafts + draft.treadles := *treadles + draft.width := *shafts + draft.height := *treadles + draft.tieup := tieup + draft.color_list := ["black", "white"] + draft.warp_colors := list(*threading, 1) + draft.weft_colors := list(*treadling, 2) + + write(xencode(draft)) + +end + +procedure tromp(treadle) + local result + + result := "" + + treadle ? { + every result ||:= upto("1") || "," + } + + return result[1:-1] + +end + +procedure examine(array) + local count, lines, line + + lines := table() # table to be keyed by line patterns + count := 0 + + every line := !array do # process lines + /lines[line] := (count +:= 1) # if new line, insert with new number + + return lines + +end diff --git a/ipl/gprogs/rstarlab.icn b/ipl/gprogs/rstarlab.icn new file mode 100644 index 0000000..c27b7bf --- /dev/null +++ b/ipl/gprogs/rstarlab.icn @@ -0,0 +1,64 @@ +############################################################################ +# +# File: rstarlab.icn +# +# Subject: Program to draw regular stars +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws regular stars. See +# +# Geometric and Artistic Graphics; Design Generation with +# Microcomputers, Jean-Paul Delahaye, Macmillan, 1987, pp. 5-7. +# +# The window is square. The window size can be given on the command line, +# default 600. +# +# The present user interface is crude. To see all the regular stars +# that are provided by default, type +# +# all +# +# from standard input. After each star is drawn, the program waits +# for an event before going on to the next star. +# +# Alternatively, a single star can be drawn by typing its name preceded +# by an equals sign. The names are rstar01 through rstar06. For example, +# +# =rstar02 +# +# draws the second star. +# +# In future extensions, provision will be made for user-defined stars. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: drawlab, rstars, rstartbl +# +############################################################################ + +link drawlab +link rstars +link rstartbl + +global size + +procedure main(argl) + + size := integer(argl[1]) | 600 + + drawlab(rstar, rstartbl, "regular stars") + +end diff --git a/ipl/gprogs/scroll.icn b/ipl/gprogs/scroll.icn new file mode 100644 index 0000000..968160a --- /dev/null +++ b/ipl/gprogs/scroll.icn @@ -0,0 +1,105 @@ +############################################################################ +# +# File: scroll.icn +# +# Subject: Program to scroll image +# +# Author: Jon Lipp +# +# Date: November 22, 1996 +# +########################################################################## +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays an image, with scolling. +# +########################################################################## +# +# Links: options, vidgets, vscroll, wopen, xcompat +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ + +link options +link vidgets, vscroll +link wopen +link xcompat + +global win, im_win, view_width, view_height +global scv, sch + +procedure main(args) + local opts, file, scrollbar_width, picw, pich, root + + opts := options(args, "f:w+h+") + file := \opts["f"] | + stop("Usage: scroll -f file [-w window size/width] [-h window height]") + view_width := \opts["w"] | 300 + view_height := \opts["h"] | view_width + scrollbar_width := 15 +# +# Load in the bitmap; get the dimensions. +# + im_win := WOpen("canvas=hidden", "image=" || file) | + stop("Couldn't make temporary bitmap.") + picw := WAttrib(im_win, "width") + pich := WAttrib(im_win, "height") + + win := WOpen("label=" || file, "size=" || + (view_width + scrollbar_width + 1) || "," || + (view_height + scrollbar_width + 1) ) | + stop("*** cannot open file") + + root := Vroot_frame(win) +# +# Create two scrollbars. +# + scv := Vvert_scrollbar(root, -1, 0, win, sl_cb, 1, + view_height,scrollbar_width, pich, 0, , view_height) + sch := Vhoriz_scrollbar(root, 0, -1, win, sl_cb, 2, view_width, + scrollbar_width, 0, picw, , view_width) + + VResize(root) +# +# Draw the initial view of the pixmap, based on the scrollbar's values. +# + sl_cb(scv, scv.callback.value) + sl_cb(sch, sch.callback.value) +# +# Now get events, pass control to the procedure quit() if an event is not +# captured by a vidget. +# + GetEvents(root, quit, , resize) +end + +# +# Terminate the program on a keypress of "q". +# +procedure quit(e) + + if e === "q" then stop("End scroll.") +end + +procedure resize(root) + + VReformat(scv, WAttrib(scv.win, "height") - 15) + VReformat(sch, WAttrib(sch.win, "width") - 15) +end + +# +# Copy a portion of the bitmap to the main +# window based on the values of the scrollbars. +# +procedure sl_cb(caller, val) + static vpos, hpos + initial vpos := hpos := 0 + + (caller.id = 1, vpos := val) | hpos := val + CopyArea(im_win, win, hpos, vpos, view_width, view_height, 0, 0) +end diff --git a/ipl/gprogs/scroller.icn b/ipl/gprogs/scroller.icn new file mode 100644 index 0000000..53ed308 --- /dev/null +++ b/ipl/gprogs/scroller.icn @@ -0,0 +1,48 @@ +############################################################################ +# +# File: scroller.icn +# +# Subject: Program to scroll image +# +# Author: Ralph E. Griswold +# +# Date: October 4, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(args) + local width, height, win1, win2 + + win1 := WOpen("image=" || args[1]) | stop("*** cannot open image") + + height := WAttrib(win1, "height") + width := WAttrib(win1, "width") + + win2 := WOpen("canvas=hidden", "size=1," || height) + + repeat { + CopyArea(win1, win2, 0, 0, 1, height) + CopyArea(win1, win1, 1, 0, width - 1, height) + CopyArea(win2, win1, 0, 0, 1, height, width - 1, 0) + WDelay(10) + } + +end diff --git a/ipl/gprogs/seamcut.icn b/ipl/gprogs/seamcut.icn new file mode 100644 index 0000000..f75e6c4 --- /dev/null +++ b/ipl/gprogs/seamcut.icn @@ -0,0 +1,70 @@ +############################################################################ +# +# File: seamcut.icn +# +# Subject: Program to cut image for seamless tiling +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes image file names and does top/bottom separation and +# reordering, follows by the same for left and right. The result is an +# image that tiles seamlessly, although the center part may be a mess. +# +# The technique is described in Painter 2.0 Companion. +# +# Files are expected to have the suffix .gif. The corresponding files +# are given the suffix _s.gif. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, wopen +# +############################################################################ + +link basename +link wopen + +procedure main(args) + local name, base, width, height, half_width, half_height, win1, win2 + + every name := !args do { + base := basename(name, ".gif") | { + write(&errout, "*** unexpected file extension ", name) + next + } + win1 := WOpen("canvas=hidden", "image=" || name) | { + write(&errout, "*** cannot open ", name) + next + } + + width := WAttrib(win1, "width") + height := WAttrib(win1, "height") + half_width := width / 2 + half_height := height / 2 + + win2 := WOpen("canvas=hidden", "width=" || width, "height=" || height) | + stop("*** cannot open target window") + + CopyArea(win1, win2, 0, 0, half_width, height, half_width, 0) + CopyArea(win1, win2, half_width, 0, half_width, height, 0, 0) + EraseArea(win1) + CopyArea(win2, win1, 0, 0, width, half_height, 0, half_height) + CopyArea(win2, win1, 0, half_height, width, half_height, 0, 0) + WriteImage(win1, base || "_s.gif") + WClose(win1) + WClose(win2) + } + +end diff --git a/ipl/gprogs/selectle.icn b/ipl/gprogs/selectle.icn new file mode 100644 index 0000000..594c393 --- /dev/null +++ b/ipl/gprogs/selectle.icn @@ -0,0 +1,571 @@ +############################################################################ +# +# File: selectle.icn +# +# Subject: Program to select tile from an image +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program is designed to assist in locating areas within an image +# that, when tiled, produce a desired effect. For example, a background +# may consist of a tiled image; this program can be used to find the +# smallest tile for the repeat (by "eye-balling"). +# +# Another interesting use of this program is to produce striped patterns by +# selecting a row or column of an image to get a tile that is one character +# wide. Sometimes a few rows or columns give an interesting "fabric" +# effect. +# +# The following features are provided through keyboard shortcuts, +# the File menu, and in some cases, on-board buttons: +# +# @D user-drawn selection rectangle +# @O open new source image +# @P pick a source image from GIF files in the current directory +# @Q quit application +# @S save current selection as an image +# @T tile selection into source image window +# +# Buttons provide for setting and adjusting the selection in various +# ways. +# +# In the drawing mode, the mouse can be used to make a selection by +# dragging from one corner to another. When the mouse is released, +# the action depends on the user keypress: +# +# "r" return the selection +# "n" try again +# "q" exit drawing mode +# +# Typing "q" is the only way to get out of the drawing mode. It can be +# done whether or not there is a selection. +# +# Notes: +# +# The selection starts as a single pixel in the upper-left corner. +# The repeat window can be resized by the user. +# +############################################################################ +# +# Features to add/improve: +# +# show current selection +# file-system navigation +# chained selection dialogs for large numbers of files +# *or* scrolling line dialog +# add flips, rotations, and other transformations (using external +# utilities) +# allow images of types other than GIF +# +# Bugs: +# width and height setting should take into account the current +# origin +# edit in system menu is bogus (bug is in interact.icn) +# +# +############################################################################ +# +# Requires: Version 9 graphics, UNIX (for "pick" feature) +# +############################################################################ +# +# Links: grecords, interact, io, select, tile +# +############################################################################ + +link grecords +link interact +link io +link select +link tile + +# To do: alphabetize the following globals + +global pattern # repeat window +global source # source window hidden +global screen # source window visible +global vidgets # table of interface vidgets +global root # root vidget +global controls + +global text # label with respect to which information is written + +global posx # x position relative to interface window +global posy # y position relative to repeat window +global wmax # maximum width of source image +global hmax # maximum height of source image + +global auto # auto-save toggle +global prefix # auto-save prefix +global count # auto-save count +global name # image name +global draw # draw vidget +global current # current selection + +$define PosX "posx=10" +$define PosY "posy=10" + +procedure main() + local atts + + atts := ui_atts() + + # The interface window is opened with a hidden canvas so that it + # can be made the active window later by making it visible. + + put(atts, "canvas=hidden", PosX, PosY) + + controls := (WOpen ! atts) | stop("*** cannot open window") + vidgets := ui() + + init() + + GetEvents(root, , shortcuts) + +end + +# Auto-save callback toggle. + +procedure auto_cb(vidget, value) + + auto := value + + if \auto then { + if OpenDialog("Specify prefix for auto-saving:") == "Cancel" then fail + prefix := dialog_value + count := -1 # initial count less 1 + } + + return + +end + +# Callback that handles all the buttons that change x, y, w, and h. + +procedure change_cb(vidget) + + # Cute code alert. The selected reversible assignment is performed + # and passed to check(). It checks the resulting selection rectangle + # and fails if it's not valid. That failure causes the reversible + # assignment to be undone and the expression fails, leaving the + # selection as it was. + + check( + case vidget.s of { + "h +": current.h <- current.h + 1 + "h -": current.h <- current.h - 1 + "w +": current.w <- current.w + 1 + "w -": current.w <- current.w - 1 + "w + h +": current.h <- current.h + 1 & current.w <- current.w + 1 + "w - h -": current.h <- current.h - 1 & current.w <- current.w - 1 + "h max": current.h <- hmax + "w max": current.w <- wmax + "w h max": current.h <- hmax & current.w <- wmax + "x +": current.x <- current.x + 1 + "x -": current.x <- current.x - 1 + "y +": current.y <- current.y + 1 + "y -": current.y <- current.y - 1 + "x + y +": current.x <- current.x + 1 & current.y <- current.y + 1 + "y - x -": current.y <- current.y - 1 & current.x <- current.x - 1 + "x 1/2": current.x <- wmax / 2 + "y 1/2": current.y <- hmax / 2 + "x y 1/2": current.x <- wmax / 2 & current.y <- hmax / 2 + } + ) | fail + + show() + + return + +end + +# Check validity of selection. + +procedure check() + + if (0 <= current.h <= hmax) & + (0 <= current.w <= wmax) & + (0 <= current.x <= hmax) & + (0 <= current.y <= wmax) + then return else { + Alert() + fail + } + +end + +# Copy hidden source window to a visible window. + +$define Margin 20 + +procedure copy_source(label) + + screen := WOpen("size=" || WAttrib(source, "width") || "," || + WAttrib(source, "height"), "posx=" || posx, "posy=" || posy, + "label=" || label) | ExitNotice("Cannot open image window") + + CopyArea(source, screen) + + expose(controls) + + wmax := WAttrib(source, "width") + hmax := WAttrib(source, "height") + + WAttrib(pattern, "width=" || (WAttrib(screen, "width") + Margin)) + WAttrib(pattern, "height=" || (WAttrib(screen, "height") + Margin)) + + reset_cb() + + return + +end + +# Enable user-drawn selection. + +procedure draw_cb(vidget, value) + local sel + + if /value then return + + if /source then { + Notice("No source image.") + SetVidget(draw, &null) + fail + } + + expose(screen) + + while current := select(screen) do + show() + + SetVidget(draw, &null) + + expose(controls) + + return + +end + +# File menu callback. + +procedure file_cb(vidget, value) + + case value[1] of { + "open @O": get_image() + "pick @P": pick() + "quit @Q": exit() + "save @S": snap() + "tile @T": tile_selection() + } + + return + +end + +# Utility procedure to get new source image. + +procedure get_image() + + WClose(\source) + WClose(\screen) + + repeat { + (OpenDialog("Open image:") == "Okay") | fail + source := WOpen("canvas=hidden", "image=" || dialog_value) | { + Notice("Can't open " || dialog_value || ".") + next + } + copy_source(dialog_value) + wmax := WAttrib(source, "width") + hmax := WAttrib(source, "height") + break + } + + return + +end + +# These values are for Motif; they may need to be changed for other +# window managers. + +$define Offset1 32 +$define Offset2 82 + +# Initialize the program + +$define MinSize 600 + +procedure init() + local iheight + + current := rect(0, 0, 1, 1) + hmax := wmax := 0 + + posx := WAttrib("width") + Offset1 + + iheight := WAttrib("height") + + pattern := WOpen("label=repeat", "resize=on", "size=" || iheight || + "," || iheight, "posx=" || posx, PosY) | + stop("*** cannot open window for repeat ***") + + posy := WAttrib(pattern, "height") + Offset2 + + root := vidgets["root"] + text := vidgets["text"] + draw := vidgets["draw"] + + WAttrib("canvas=normal") + + auto := &null + + return + +end + +# Utility procedure to let user pick an image file in the current directory. + +procedure pick() + local plist, ls + + plist := filelist("*.gif *.GIF") | + return FailNotice("Pick not supported on this platform") + + if *plist = 0 then return FailNotice("No files found.") + + repeat { + if SelectDialog("Select image file:", plist, plist[1]) == "Cancel" + then fail + WClose(\source) + WClose(\screen) + source := WOpen("canvas=hidden", "image=" || dialog_value) | { + Notice("Cannot open " || dialog_value || ".") + next + } + copy_source(dialog_value) + break + } + + return + +end + +# Callback to terminate program execution. + +procedure quit_cb() + + exit() + +end + +# Callback to reset x, y, w, and h to initial values. + +procedure reset_cb() + + current := rect(0, 0, 1, 1) + + show() + + return + +end + +# Callback procedure to save the current selection as an image file. + +procedure save_cb() + + snap() + +end + +# Callback procedure to allow use of standard tile sizes. + +procedure select_cb(vidget, value) + + check(current.w := current.h := case value of { + " 4 x 4": 4 + " 8 x 8": 8 + " 16 x 16": 16 + " 32 x 32": 32 + " 64 x 64": 64 + " 72 x 72": 72 + " 96 x 96": 96 + " 100 x 100": 100 + " 128 x 128": 128 + " 256 x 256": 256 + " 400 x 400": 400 + " 512 x 512": 512 + }) | fail + + show() + + return + +end + +# Callback to allow setting of specific selection rectangle values. + +procedure set_cb() + + repeat { + if TextDialog("Set values:", + ["x", "y", "w", "h"], + [ current.x, + current.y, + current.w, + current.h + ] + ) == "Cancel" then fail + check( + current.x <- integer(dialog_value[1]) & + current.y <- integer(dialog_value[2]) & + current.w <- integer(dialog_value[3]) & + current.h <- integer(dialog_value[4]) + ) | { + Notice("Invalid value") + next + } + show() + return + } + +end + +# Keyboard shortcuts. + +procedure shortcuts(e) + + if &meta then + case map(e) of { # fold case + "d": SetVidget(draw, 1) + "o": get_image() + "p": pick() + "q": exit() + "s": snap() + "t": tile_selection() + } + + return + +end + +# Procedure to handle all that goes with a new selection. + +# These constants are ad hoc. + +$define Width 200 +$define Height 30 +$define YOff 10 + +procedure show() + static sx, sy + + initial { + sx := text.ax + sy := text.ay + } + + if /source then return FailNotice("No source image.") + + tile(source, pattern, current.x, current.y, current.w, current.h) + + if \auto then { + name := prefix || right(count +:= 1, 3, "0") || ".gif" + WriteImage(source, name, current.x, current.y, current.w, current.h) + } + + EraseArea(sx, sy, Width, Height) + + DrawString(sx, sy + YOff, "x=" || current.x || " y=" || current.y || + " w=" || current.w || " h=" || current.h) + + if \auto then DrawString(sx, sy + 30, "last auto-save: " || name) + + return + +end + +# Utility procedure to save current selection. + +procedure snap() + + return snapshot(\source, current.x, current.y, current.w, current.h) | + FailNotice("No source image.") + +end + +# Callback for System menu. + +procedure system_cb(vidget, value) + + case value[1] of { + "edit": edit_file() + "execute": execute() + } + + return + +end + +procedure tile_selection() + + tile(pattern, screen, current.x, current.y, current.w, current.h) + CopyArea(screen, source) + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=397,360", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,397,360:",], + ["auto save:Button:regular:1:12,74,70,20:auto save",auto_cb], + ["draw:Button:regular:1:20,172,50,20:draw",draw_cb], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["open @O","pick @P","save @S ","tile @T","quit @Q"]], + ["hmax:Button:regular::205,54,56,20:h max",change_cb], + ["hminus:Button:regular::169,106,35,20:h -",change_cb], + ["hplus:Button:regular::168,80,35,20:h +",change_cb], + ["line1:Line:::0,25,400,25:",], + ["quit:Button:regular::19,311,50,20:quit",quit_cb], + ["reset_cb:Button:regular::20,116,50,20:reset",reset_cb], + ["save:Button:regular::19,40,50,20:save",save_cb], + ["select:Choice::12:285,29,99,252:",select_cb, + [" 4 x 4"," 8 x 8"," 16 x 16"," 32 x 32"," 64 x 64", + " 72 x 72"," 96 x 96"," 100 x 100"," 128 x 128"," 256 x 256", + " 400 x 400"," 512 x 512"]], + ["set:Button:regular::20,143,50,20:set",set_cb], + ["system:Menu:pull::37,1,50,21:System",system_cb, + ["edit","execute"]], + ["text:Button:regularno::112,290,154,20:current specification",], + ["whmax:Button:regular::206,80,56,20:w h max",change_cb], + ["whminus:Button:regular::108,54,56,20:w - h -",change_cb], + ["whplus:Button:regular::108,30,56,20:w + h +",change_cb], + ["wmax:Button:regular::206,29,56,20:w max",change_cb], + ["wminus:Button:regular::168,54,35,20:w -",change_cb], + ["wplus:Button:regular::168,29,35,20:w +",change_cb], + ["xhalf:Button:regular::213,153,56,20:x 1/2",change_cb], + ["xminus:Button:regular::173,180,35,20:x -",change_cb], + ["xplus:Button:regular::172,153,35,20:x +",change_cb], + ["xyhalf:Button:regular::212,206,56,20:x y 1/2",change_cb], + ["xyminus:Button:regular::109,181,56,20:x - y +",change_cb], + ["xyplus:Button:regular::110,151,56,20:x + y +",change_cb], + ["y minus:Button:regular::172,231,35,20:y -",change_cb], + ["y plus:Button:regular::173,206,35,20:y +",change_cb], + ["yhalf:Button:regular::212,177,56,20:y 1/2",change_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/sensdemo.icn b/ipl/gprogs/sensdemo.icn new file mode 100644 index 0000000..c9ade64 --- /dev/null +++ b/ipl/gprogs/sensdemo.icn @@ -0,0 +1,157 @@ +############################################################################ +# +# File: sensdemo.icn +# +# Subject: Program to demonstrate sensor routines +# +# Author: Gregg M. Townsend +# +# Date: July 12, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# sensdemo illustrates several of the input sensors provided in the +# program library. It is written to use mutable colors but will struggle +# along slowly if they're not available. +# +# There are four pushbuttons. Buttons "One", "Two", and "Three" just +# write a line on standard output. The "QUIT" button does what you'd +# expect. +# +# The three vertically oriented sliders control (from left to right) +# alter the red, green, and blue components of the color in the large +# square. The individual components appear in the small squares, and +# the hexadecimal form of the color spec is displayed below the square. +# +# The small horizontal slider below the square adjusts all three +# color components simultaneously. Notice how moving it also moves +# the three vertical sliders. +# +# The largs square sounds a bell if Return is pressed while it +# contains the cursor. The standard "quitsensor" causes the program +# to exit when q or Q is pressed anywhere in the window. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, slider, evmux, graphics +# +############################################################################ + +link button +link slider +link evmux +link graphics + +$define BevelWidth 2 +$define WindowMargin 10 + +record rgbrec(r, g, b, k) +record boxrec(x, y, w, h, b, i) +global val, colr, sl, win + +procedure main(args) + local cwin, h, m, c + + # open window + win := Window("size=400,400", args) + m := WindowMargin + h := WAttrib("height") - 2 * m # usable height + + # set up boxes for displaying colors, each with its own binding + colr := rgbrec( + boxrec(m, m, 40, 40), + boxrec(m, m + 55, 40, 40), + boxrec(m, m + 110, 40, 40), + boxrec(m + 65, m, 150, 150)) + every c := !colr do { + c.b := Clone(win) + Bg(c.b, c.i := NewColor(win)) # fails if b/w screen + BevelRectangle(win, c.x, c.y, c.w, c.h, -BevelWidth) + EraseArea(c.b, + c.x+BevelWidth, c.y+BevelWidth, c.w-2*BevelWidth, c.h-2*BevelWidth) + } + + # set up sliders to control the colors + val := rgbrec(0.1, 0.8, 1.0, 0.8) # initial positions + sl := rgbrec() + sl.r := slider(win, setrgb, 1, 290, m, 20, h, 0.0, val.r, 1.0) + sl.g := slider(win, setrgb, 2, 330, m, 20, h, 0.0, val.g, 1.0) + sl.b := slider(win, setrgb, 3, 370, m, 20, h, 0.0, val.b, 1.0) + sl.k := slider(win, setgray, 4, m+65, m+160, 150, 14, 0.0, 0.8, 1.0) + setcolors() # download the colors + + # set up miscellaneous sensors + quitsensor(win) # quit on q or Q + sensor(win, '\r', ding, &null, m+65, m, 150, 150) # \r in box sounds bell + buttonrow(win, 150, 250, 100, 20, 0, 30, # vertical button row + "One", bpress, "one", + "Two", bpress, "two", + "Three", bpress, "three", + ) + button(win, "QUIT", argless, exit, m, m+h-60, 60, 60) # and a QUIT button + + # enter event loop + evmux(win) +end + +procedure bpress(win, a) # echo a button press + write("button ", a) + return +end + +procedure ding(win, a, x, y, k) # ring the bell + writes("\^g") + flush(&output) + return +end + +procedure setcolors() # set the colors in the color map + colorbox(colr.r, 65535 * val.r, 0, 0) + colorbox(colr.g, 0, 65535 * val.g, 0) + colorbox(colr.b, 0, 0, 65535 * val.b) + colorbox(colr.k, 65535 * val.r, 65535 * val.g, 65535 * val.b) + GotoXY(win, 100, 200) + write(win, "color = #", hexv(val.r), hexv(val.g), hexv(val.b)) + return +end + +procedure colorbox(box, r, g, b) + r := integer(r) + g := integer(g) + b := integer(b) + if \box.i then + Color(box.b, box.i, r || "," || g || "," || b) + else { + Shade(box.b, r || "," || g || "," || b) + FillRectangle(box.b, box.x+1, box.y+1, box.w-1, box.h-1) + } + return +end + +procedure hexv(v) # two-hex-digit specification of v + static hextab + initial { + every put((hextab := []), !"0123456789ABCDEF" || !"0123456789ABCDEF") + } + return hextab [integer(255 * v + 1.5)] +end + +procedure setgray(win, i, v) # set a grayvalue of v + every i := 1 to 3 do + slidervalue(sl[i], val[i] := v) + setcolors() + return +end + +procedure setrgb(win, i, v) # set color component i to value v + val[i] := v + setcolors() +end diff --git a/ipl/gprogs/showcolr.icn b/ipl/gprogs/showcolr.icn new file mode 100644 index 0000000..d41e6e2 --- /dev/null +++ b/ipl/gprogs/showcolr.icn @@ -0,0 +1,37 @@ +############################################################################ +# +# File: showcolr.icn +# +# Subject: Program to list colors in Icon palettes +# +# Author: Ralph E. Griswold +# +# Date: March 14, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program produces files of color specifications for all of Icon's +# built-in palettes. The output is written to a file whose base name is +# the palette and whose suffix is ".clr". +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ + +procedure main() + local palette, output + + every palette := ("c" || (1 to 6)) | ("g" || (2 to 256)) do { + output := open(palette || ".clr", "w") | + stop("*** cannot open output file for palette ", palette) + every write(output, PaletteColor(palette, !PaletteChars(palette))) + close(output) + } + +end diff --git a/ipl/gprogs/showtile.icn b/ipl/gprogs/showtile.icn new file mode 100644 index 0000000..339a6d1 --- /dev/null +++ b/ipl/gprogs/showtile.icn @@ -0,0 +1,194 @@ +############################################################################ +# +# File: showtile.icn +# +# Subject: Program to display tiles +# +# Author: Ralph E. Griswold +# +# Date: June 10, 1999 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program displays pattern tiles given in standard input. +# +# The options are: +# +# -P show pattern produced by tile; default show tile +# -i s create image files with prefix s +# -a run without waiting for event in window +# -u don't show on-screen images; implies -a +# -p i start with page i +# -r i number of rows, default 7 for -P, otherwise 10 +# -c i number of columns, default 6 for -P, otherwise 12 +# -n s number pages using s as a prefix +# -w i width of area for tile; default 48 unless -P +# -h i height of area for file; default 48 unless -P +# -d add date line +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, patutils, xio, xutils, graphics, xcompat +# +############################################################################ + +link options +link patutils +link xio +link xutils +link graphics +link xcompat + +procedure main(args) + local x, y, w, h, pattern, count, page, opts, images, auto, unseen, foot + local rows, cols, prefix, bfont, nfont, dims, areaw, areah, signal, poff + local date, HGap, VGap, patterns + + opts := options(args, "Pi:aup+r+c+w+h+n:d") + + images := \opts["i"] + auto := \opts["a"] + auto := unseen := \opts["u"] + page := (\opts["p"] - 1) | 0 + prefix := \opts["n"] + if \opts["d"] then date := &dateline else date := "" + foot := \prefix | \opts["d"] + + if \opts["P"] then { # pattern mode + patterns := 1 + HGap := 32 # gap between + VGap := 32 # gap below + areaw := 128 # pattern width + areah := 64 # pattern height + rows := \opts["r"] | 7 + cols := \opts["c"] | 6 + w := (areaw + HGap) * cols - HGap + h := (areah + VGap) * rows + if \foot then h +:= 20 + } + else { # image mode + HGap := 16 # gap between + VGap := 16 # gap below + rows := \opts["r"] | 10 + cols := \opts["c"] | 12 + areaw := \opts["w"] | 48 + areah := \opts["h"] | 48 + w := (areaw + HGap) * cols + 1 + h := (areah + VGap) * rows + 1 + if \foot then h +:= 20 # space for page number + } + + WOpen("width=" || w, "height=" || h, "canvas=hidden") | + stop("*** cannot open window") + if /unseen then WAttrib("canvas=normal") + + if \patterns then WAttrib("fillstyle=textured") + + bfont := "-misc-fixed-medium-r-normal--10-100-75-75-c-60-iso8859-1" + nfont := "-misc-fixed-medium-r-normal--15-140-75-75-c-90-iso8859-1" + + Font(bfont | "6x10" | "fixed") + + count := 0 + +# Skip pages if requested. + + every 1 to (rows * cols) * page do { + readpatt() | stop("*** premature end of file") + count +:= 1 + } + +# Main processing loop. + + repeat { + if \patterns then EraseArea() + else grid(areaw + HGap, areah + VGap, cols, rows) + + x := y := 0 + +# Do a page. + + every 1 to rows do { + every 1 to cols do { + pattern := readpatt() | break break break + count +:= 1 + if \patterns then { + Pattern(pattern) | { + write(&errout, "*** could not set pattern: ", pattern) + next + } + FillRectangle(x, y, areaw, areah) + GotoXY(x, y + areah + VGap / 3) + WWrites(left(count || ":", 5)) + dims := tiledim(pattern) + WWrites(left(dims.w || "x" || dims.h, 7)) + WWrites("d=", left(pdensity(pattern), 7)) + GotoXY(x, y + areah + VGap / 3 + 11) + if *pattern > 20 then pattern := pattern[1+:18] || "..." + WWrites(pattern) + } + else { + poff := (HGap + areaw - tiledim(pattern).w) / 3 + DrawImage(x + poff, y + VGap / 2, pattern) + WFlush() + CenterString(x + poff * 2, y + areah + VGap / 3, count) + } + x +:= areaw + HGap + } + x := 0 + y +:= areah + VGap + } + + page +:= 1 + if \foot then { + GotoXY(0, h - 5) + Font(nfont | "10x20" | "fixed") # numbering font + WWrites(\prefix || page) + GotoXY(w - TextWidth(date), h - 5) + WWrites(date) + Font(bfont | "6x10" | "fixed") # restore body font + } + if /auto & /unseen then signal := Event() + WriteImage(\images || right(page, 2, "0") || ".gif") + if signal === "q" then exit() + } + + page +:= 1 + if \foot then { + GotoXY(0, h - 5) + Font(nfont | "10x20" | "fixed") # numbering font + WWrites(\prefix || page) + GotoXY(w - TextWidth(date), h - 5) + WWrites(date) + } + WriteImage(\images || right(page, 2, "0") || ".gif") + if /auto then WDone() + +end + +# Draw a grid for the tile mode + +procedure grid(w, h, c, r) + local wc, hr, x, y + + wc := w * c + hr := h * r + + EraseArea() + + every x := 0 to wc by w do + DrawLine(x, 0, x, hr) + every y := 0 to hr by h do + DrawLine(0, y, wc, y) + + return + +end diff --git a/ipl/gprogs/sier.icn b/ipl/gprogs/sier.icn new file mode 100644 index 0000000..a0b48cf --- /dev/null +++ b/ipl/gprogs/sier.icn @@ -0,0 +1,218 @@ +############################################################################ +# +# File: sier.icn +# +# Subject: Program for generalized Sierpinski's triangle +# +# Author: Gregg M. Townsend +# +# Date: June 10, 2004 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Originally inspired by the Nova television show on chaos. +# Colorization suggested by Kenneth Walker. +# +############################################################################ +# +# This program constructs Sierpinski's triangle using an iterative +# method. An initial point is chosen (by clicking the mouse inside the +# triangle) and marked. Then, the program repeatedly moves half way to +# a randomly chosen vertex and plots a point in the color corresponding +# to the vertex. +# +# The polygon need not be a triangle. The number of sides may be given +# as a command line argument, or a digit 3 through 9 or 0 through 2 may be +# pressed to establish a new polygon of 3 to 12 sides. +# +# The S, G, E, and Q keys function identically to the Stop, Go, Erase, +# Quit pushbuttons. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: button, evmux, random, graphics +# +############################################################################ + +link button +link evmux +link random +link graphics + +$define BevelWidth 2 +$define WindowMargin 10 + +global win, bwin, vwin, vcolors +global m, w, h +global nsides, xpos, ypos, outline +global running, xcur, ycur + +procedure main(args) + local i, vcolors + + win := Window("size=400,400", "font=Helvetica,bold,14", "bg=pale gray", args) + nsides := integer(args[1]) | 3 + if nsides < 3 then stop("sierpinski: need at least 3 sides!") + + m := WindowMargin + h := WAttrib("height") - 2 * m # usable height + w := WAttrib("width") - 2 * m # usable width + + # make a window (g.c.) for drawing in background color + bwin := Clone(win) + Fg(bwin, Bg(win)) + + # make a color for each vertex + vcolors := [ + "deep green", + "dark red", + "dark blue", + "deep red-magenta", + "dark cyanish blue", + "dark red-orange", + "deep purple", + "deep cyan", + "deep brown", + "deep orangish red", + "deep purple", + "dark cyanish blue" + ] + vwin := [] + if WAttrib(win, "depth") > 2 then + every put(vwin, Clone(win, "fg=" || !vcolors)) + else + put(vwin, win) + + # configure and draw the polygon + configure() + erase() + + # set up buttons and character handlers + button(win, "Go", setfill, 0, m, m, 50, 20) + button(win, "Stop", setfill, -1, m, m + 30, 50, 20) + button(win, "Erase", argless, erase, m + w - 50, m, 50, 20) + button(win, "Quit", argless, exit, m + w - 50, m + 30, 50, 20) + sensor(win, 'Gg', setfill, 0) + sensor(win, 'Ss', setfill, -1) + sensor(win, 'Ee', argless, erase) + quitsensor(win) # enable Q-for-quit etc. + sensor(win, '3456789012', setsides) + + # set up sensor for drawing the curve + sensor(win, &lrelease, setfill, 1, m, m, w, h) + + # process events + randomize() + i := 1 + repeat { + while *Pending(win) > 0 | running < 0 do + evhandle(win) + every 1 to 100 do { + DrawPoint(vwin [i | 1], xcur, ycur) + i := ?nsides + xcur := (xcur + xpos[i]) / 2 + ycur := (ycur + ypos[i]) / 2 + } + } +end + + + +# configure() -- set vertex points + +procedure configure() + local a, da, i + local xmin, xmax, xscale, ymin, ymax, yscale + + # ensure we have enough windows for the vertices + while *vwin < nsides do + vwin |||:= vwin + + # get coordinates for vertices as points on a radius-1 circle + da := 2 * &pi / nsides + a := 1.5 * &pi - da / 2 + if nsides = 4 then + a +:= &pi / 12 + xpos := list(nsides) + ypos := list(nsides) + every i := 1 to nsides do { + xpos[i] := cos(a) + ypos[i] := sin(a) + a -:= da + } + + # now scale to available window size + # also make coord list for drawing outline + xmin := xmax := ymin := ymax := 0.0 + every xmin >:= !xpos + every xmax <:= !xpos + every ymin >:= !ypos + every ymax <:= !ypos + xscale := w / (xmax - xmin) + yscale := h / (ymax - ymin) + outline := [win] + every i := 1 to nsides do { + put(outline, m + xscale * (1.01 * xpos[i] - xmin)) + put(outline, m + h - yscale * (1.01 * ypos[i] - ymin)) + xpos[i] := m + xscale * (xpos[i] - xmin) + ypos[i] := m + h - yscale * (ypos[i] - ymin) + } + put(outline, outline[2]) + put(outline, outline[3]) +end + + + +# erase(gc) -- erase the polygon and draw its outline + +procedure erase(gc) + outline[1] := bwin + FillPolygon ! outline + outline[1] := \gc | win + DrawLine ! outline + running := -1 + xcur := m + w / 2 + ycur := m + h / 2 + return +end + + + +# setfill(win, n, x, y) -- start/stop filling points according to n +# +# n<0 stop +# n=0 start, from current point +# n>0 start, from (x,y) + +procedure setfill(win, n, x, y) + if n > 0 then { + xcur := x + ycur := y + } + if n >= 0 then { + outline[1] := bwin + DrawLine ! outline # erase outline + } + running := n + return +end + + + +# setsides(win, dummy, x, y, event) - reset the number of sides + +procedure setsides(win, dummy, x, y, event) + nsides := integer(event) + if nsides < 3 then nsides +:= 10 + erase(bwin) + configure() + erase() +end diff --git a/ipl/gprogs/sier1.icn b/ipl/gprogs/sier1.icn new file mode 100644 index 0000000..af02464 --- /dev/null +++ b/ipl/gprogs/sier1.icn @@ -0,0 +1,50 @@ +############################################################################ +# +# File: sier1.icn +# +# Subject: Program to draw the Sierpinski triangle +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program demonstrates an interesting way to draw the Sierpinski +# triangle. For an explanation, see +# +# Chaos and Fractals, Heinz-Otto Peitgen, Harmut Jurgens, +# and Dietmar Saupe, Springer-Verlah, 1992, pp. 132-134. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main() + local width, offset, x, y + + WOpen("label=sierpinski", "size=300,300") | + stop("*** cannot open window") + + width := 256 + offset := 30 + + every y := 0 to width - 1 do + every x := 0 to width - 1 do + if iand(x, y) = 0 then DrawPoint(x + offset, y + offset) + + Event() + +end diff --git a/ipl/gprogs/sier2.icn b/ipl/gprogs/sier2.icn new file mode 100644 index 0000000..2ae6ba2 --- /dev/null +++ b/ipl/gprogs/sier2.icn @@ -0,0 +1,68 @@ +############################################################################ +# +# File: sier2.icn +# +# Subject: Program to display the Sierpinski fractal +# +# Author: Ralph E. Griswold +# +# Date: June 24, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is a barebones version of a display of the Sierpinski fractal. +# It has deliberately been left simple and free of options so that the +# basic idea is clear and so that it can be used as the basis of +# more capable versions. +# +# This program is based on material given in "Chaos, Fractals, +# and Dynamics", Robert L. Devaney, Addison-Wesley, 1990. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main() + local extent, x, y, i + + extent := 300 + + WOpen("label=sier", "height=" || extent, "width=" || extent) | + stop("*** cannot open window") + + x := 20 # The results do not depend on these values + y := 150 + + every i := 1 to 100000 do { + case ?3 of { # Decide what to do at random + 1: { + x /:= 2 + y /:= 2 + } + 2: { + x /:= 2 + y := (extent + y) / 2 + } + 3: { + x := (extent + x) / 2 + y := (extent + y) / 2 + } + } + if i > 1000 then DrawPoint(x, y) # Wait until attraction + } + + Event() + +end diff --git a/ipl/gprogs/snapper.icn b/ipl/gprogs/snapper.icn new file mode 100644 index 0000000..4411efd --- /dev/null +++ b/ipl/gprogs/snapper.icn @@ -0,0 +1,63 @@ +############################################################################ +# +# File: snapper.icn +# +# Subject: Program to display images +# +# Authors: Ralph E. Griswold and Clinton L. Jeffery +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is just a simple program to display black-and-white versions of screen +# dumps. +# +# Type the name of an XBM or XPM file on the prompt in the input window. +# Get rid of an image by click in the image window. Exit the program +# by clicking in the input window. +# +# As an exercise, you might want to make this program more versatile -- +# and perhaps write a program to do slide shows. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +procedure main(av) + local name, window, winput + + if *av > 0 then { + every name := !av do { + (window := WOpen("label=" || name, "image=" || name,"pos=400,200")) | + write(&errout,"cannot open image ",name) + } + Active() + } else { + winput := WOpen("label=snapper! (click mouse in this window to exit)") | + stop("** can't open window") + + repeat { + close(\window) + writes(winput, "next image: ") + name := read(winput) + (window := WOpen("label=" || name, "image=" || name,"pos=400,200")) | + write(winput,"cannot open image") + if Event(winput) === (&lpress | &mpress | &rpress) then + exit() + } + } + +end diff --git a/ipl/gprogs/spectra.icn b/ipl/gprogs/spectra.icn new file mode 100644 index 0000000..a7e2225 --- /dev/null +++ b/ipl/gprogs/spectra.icn @@ -0,0 +1,59 @@ +############################################################################ +# +# File: spectra.icn +# +# Subject: Program to report color spectra in images +# +# Author: Ralph E. Griswold +# +# Date: November 22, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program analyzes images whose names are given on the command line +# and produces a file with the lists of colors and number of pixels of +# each color. The entries are given in the order of most to least frequent +# color. The color files have the base name of the image file and the +# extension ".spc". +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, imgcolor, wopen +# +############################################################################ + +link imgcolor +link basename +link wopen + +procedure main(args) + local file, colors, output, name, count + + every file := !args do { + WOpen("canvas=hidden", "image=" || file) | { + write(&errout, "*** cannot open image file ", file) + next + } + colors := imgcolor() + WClose() + name := basename(file, ".gif") + output := open(name || ".spc", "w") | { + write("*** cannot open ", name, ".spc") + next + } + colors := sort(colors, 4) + while count := pull(colors) do + write(output, left(pull(colors), 20), right(count, 8)) + close(output) + &window := &null + } + +end diff --git a/ipl/gprogs/spider.icn b/ipl/gprogs/spider.icn new file mode 100644 index 0000000..0c25529 --- /dev/null +++ b/ipl/gprogs/spider.icn @@ -0,0 +1,567 @@ +############################################################################ +# +# File: spider.icn +# +# Subject: Program to play Spider solitaire card game +# +# Author: William S. Evans +# +# Date: February 19, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Initially, 54 cards are dealt (from two decks shuffled together) +# into 10 piles (6,5,5,6,5,5,6,5,5,6) with only the top card in each +# pile face-up. You may pile face-up cards in decreasing order (Ace +# is smallest) by moving the topmost face-up "run" of cards from one +# pile to another. A run is a decreasing sequence of cards in the +# same suit. To perform the move, you may drag the run to its +# destination, click on the pile containing the run, or type its +# number. In the latter two cases, the program tries to move the +# longest run in the pile to the "best" location. You may move any +# run to an empty pile. To move a partial run, drag or click its +# deepest card using the center mouse button. +# +# A run from King to Ace can be removed from the board (by clicking on +# its pile or typing its pile number). +# +# The 50 additional cards remaining in the deck may be dealt, one to +# each pile, as long as every pile contains at least one card. +# +# The goal of the game is to remove all 104 cards from the board. +# +# The following keys are recognized by the program: +# 'd' Deal. +# 'u' Undo last move or deal. +# 'q' Quit. +# 'e' Print list of face-up cards in pile. (Useful if the +# pile becomes so big that the card names are obscured.) +# 'E' Print list of face-down cards in pile. (Cheating) +# 'n' Start a new game. +# 's' Save the current game to a file. +# 'r' Read a game from a file. +# '1234567890' Move run from indicated pile. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: drawcard, graphics, random +# +############################################################################ + +link drawcard +link graphics +link random + +$define SPIDER_VERSION "spider-0.3" # version of spider + +global cardw, cardh # card width and height +global ymargin, xmargin, xgap # margins, gap between cards +global height,width,fheight,descent # window attributes +global lap # overlap of facedown cards +global deck # a string of characters +global up # list of integers +global pile # list of strings +global yoff # list of lists of integers +global nextCard # an integer +global undoStack # list of integers +global currentFile # filename to store/retrieve a game +global readingGame # =1 if reading game from file =0 o.w. + +procedure main(args) + local fromPile,maxCards,e,p + + initialize(args) + newgame() + repeat case e := Event() of { + !"qQ": { + exit() + } + "d": { + deal() | beep() + } + "e": { + message(pileNames(1+(&x-xmargin+xgap/2)/(cardw+xgap))) + } + "E": { + message(hiddenNames(1+(&x-xmargin+xgap/2)/(cardw+xgap))) + } + "n": { + newgame() + } + "u": { + undo() | beep() + } + "r": { + readingGame := 1 + WAttrib("bg=pale gray","fg=black") + readFile() + readingGame := 0 + WAttrib("bg=deep moderate green","fg=white") + drawBoard() + } + "s": { + WAttrib("bg=pale gray","fg=black") + saveFile() + WAttrib("bg=deep moderate green","fg=white") + } + !"1234567890": { + p := 0 < ord(e)-ord("0") | 10 + click(13,p,p) | beep() + } + &lpress | &rpress: { + fromPile := 1 + (&x - xmargin + xgap/2) / (cardw + xgap) + maxCards := 13 + } + &mpress: { + fromPile := 1 + (&x - xmargin + xgap/2) / (cardw + xgap) + maxCards := 1 + every &y <= !yoff[11 > fromPile] do + maxCards +:= 1 + } + &lrelease | &mrelease | &rrelease: { + click(maxCards,fromPile,1 + (&x-xmargin+xgap/2) / (cardw+xgap)) | + beep() + } +# &resize: { +# drawBoard() +# } + } + +end + +procedure initialize(args) + + currentFile := "game1.spd" + readingGame := 0 + cardw := 80 + cardh := 124 + pile := list(11) + up := list(11) + yoff := list(11) + undoStack := list(0) + + deck := "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" || + "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" + randomize() + + ymargin := 10 + xmargin := 16 + xgap := xmargin / 2 + lap := 5 # how much facedown cards overlap in pile + + WOpen("width="||(10 * cardw + 9 * xgap + 2 * xmargin),"height=500", + "bg=deep moderate green","fg=white") + if WAttrib("displaywidth") < 900 then { + xmargin := xgap := 0; + WAttrib("width=800") + } + Font("serif") + height := WAttrib("height") + width := WAttrib("width") + fheight:= WAttrib("fheight") + descent:= WAttrib("descent") + + ymargin <:= fheight + + return +end + + + +procedure newgame(initialDeck) + local i, j, s + + while (pop(undoStack)) # empty stack + + deck := \initialDeck | # use initialDeck or shuffle deck + every i := *deck to 2 by -1 do + deck[?i] :=: deck[i] + + deck ? { + pile[1] := move(6) + pile[2] := move(5) + pile[3] := move(5) + pile[4] := move(6) + pile[5] := move(5) + pile[6] := move(5) + pile[7] := move(6) + pile[8] := move(5) + pile[9] := move(5) + pile[10] := move(6) + nextCard := 55 + } + pile[11] := "" + + every i := 1 to 10 do + up[i] := 1 + up[11] := 0 + + drawBoard() + return +end + +procedure drawPiles(p[]) + local i,j,n,x,y,ht,mlap,upstart,yposns + + if readingGame = 0 then { + every i := 1 <= 10 >= !p do { + +# write("pile ",i," = ",pile[i]," up = ",up[i]) + + yoff[i] := yposns := list(0) + x := xmargin + (i-1) * (cardw + xgap) + EraseArea(x,ymargin,cardw,height-2*ymargin) + GotoXY(x+cardw/2,ymargin-descent) + WWrites(10 > i | 0) + n := *(pile[i]) + mlap := lap + if n > 1 then + mlap >:= (height - 2*ymargin - cardh)/(n-1.0) + every j := n to up[i]+1 by -1 do { + y := ymargin + (n - j)*mlap + drawcard(x,y,"-") + put(yposns, y) + } + if up[i] > 0 then { + upstart := ymargin + (n-up[i])*mlap + mlap := (height-2*ymargin-cardh-(n-up[i])*mlap)/ + (0<up[i]-1.0) | 0 + mlap >:= cardh / 3 + every j := up[i] to 1 by -1 do { + y := integer(upstart + (up[i] - j)*mlap) + drawcard(x,y,pile[i][j]) + put(yposns, y) + } + } + } + message("") + } + return +end + + +procedure drawBoard() + if readingGame = 0 then { + WAttrib("label=Spider Deck "||104-nextCard+1) + drawPiles(1,2,3,4,5,6,7,8,9,10) + } + return +end + +procedure deal() + local i + + every i := 1 to 10 do { + if *(pile[i]) = 0 then fail + } + + every i := 1 to 10 do { + pile[i] := (deck[nextCard] || pile[i]) | fail + up[i] +:= 1 + nextCard +:= 1 + } + if readingGame = 0 then { + push(undoStack,0,0,0,2) # flag for deal + drawBoard() + } + return +end + +procedure undo() + local undoFlag,i,toPile,fromPile,n + +# writes(">") +# every writes(!undoStack," ") +# write("") + + undoFlag := pop(undoStack) | fail + case undoFlag of { + 0 | 1: { # undo move + toPile := pop(undoStack) + fromPile := pop(undoStack) + n := pop(undoStack) + up[fromPile] -:= undoFlag # undoFlag = 1 means unturn card + moveNoUndo(n,toPile,fromPile) + } + 2: { # undo deal + every i := 1 to 10 do { + pile[i] := pile[i][2:0] + up[i] -:= 1 + nextCard -:= 1 + } + pop(undoStack) # push spacers + pop(undoStack) + pop(undoStack) + drawBoard() + } + default: fail # this should never happen + } + return +end + +procedure moveNoUndo(n,fromPile,toPile) +# write("moveNoUndo ",n," ",fromPile," ",toPile) + pile[toPile] := pile[fromPile][1:n+1] || pile[toPile] + up[toPile] +:= n + pile[fromPile] := (pile[fromPile][n+1:0] | "") + up[fromPile] -:= n + drawPiles(fromPile,toPile) + return +end + +procedure moveCards(n,fromPile,toPile) + push(undoStack,n) + push(undoStack,fromPile) + push(undoStack,toPile) + if n = up[fromPile] & *(pile[fromPile]) > n then { + push(undoStack,1) + up[fromPile] +:= 1 + } else { + push(undoStack,0) + } + moveNoUndo(n,fromPile,toPile) + + return +end + +procedure chainPrefix(p) + local i + + i := 1 + while (i < up[p] & \(succ(pile[p][i])) == pile[p][i+1]) do { + i +:= 1 + } + return pile[p][1:i+1] +end + + +procedure click(maxCards, fromPile, toPile) + local i,j,tail,chain,c + +# write("click ",fromPile," ",toPile) + + chain := chainPrefix(fromPile) | fail + chain := chain[1+:maxCards] # limit chain size (may fail, no effect) + 0 < toPile <= 10 | fail + 0 < fromPile <= 10 | fail + + if fromPile = toPile then { # find best pile to move to + if *chain = 13 then { # take-off entire suit + moveCards(13,fromPile,11) + return + } else { # move chain + tail := succ(chain[-1]) | &null + + i := 0 < fromPile - 1 | 10 + j := fromPile + while i ~= fromPile do { + if pile[i] == "" & j = fromPile then { + j := i + } else if pile[i][1] == \tail then { + j := i + break + } else if rank(pile[i][1]) == rank(\tail) then { + j := i + } + i := 0 < i - 1 | 10 + } + if j ~= fromPile then { + moveCards(*chain,fromPile,j) + return + } + } + } else { # move to toPile + if pile[toPile] == "" then { + moveCards(*chain,fromPile,toPile) + return + } else { + c := pile[toPile][1] + every i := 1 to *chain do { + if rank(c) == rank(chain[i]) + 1 then { + moveCards(i,fromPile,toPile) + return + } + } + } + } + cantMove(chain[-1]) +# fail +end + +procedure cantMove(c) + message("Can't move " || rankName(c) || suitName(c)) + return +end + + +# label: ABCDEFGHIJKLM NOPQRSTUVWXYZ abcdefghijklm nopqrstuvwxyz +# rank: A23456789TJQK A23456789TJQK A23456789TJQK A23456789TJQK +# suit: hearts....... spades....... clubs........ diamonds..... + +procedure suit(c) + if c >>= "A" & c <<= "M" then return "A" #hearts + if c >>= "N" & c <<= "Z" then return "N" #spades + if c >>= "a" & c <<= "m" then return "a" #clubs + if c >>= "n" & c <<= "z" then return "n" #diamonds +# fail +end + +procedure rank(c) + return ord(c)-ord(suit(c)) +end + +procedure succ(c) + if c == !"MZmz" then fail + else return char(ord(c)+1) +end + +procedure beep() + writes("\^g") + flush(&output) + return +end + +procedure rankName(c) + local r + + case r := rank(c) of { + 0: return "A" + 1 to 9: return string(r+1) + 10: return "J" + 11: return "Q" + 12: return "K" + } +end + +procedure suitName(c) + case suit(c) of { + "A": return "h" + "N": return "s" + "a": return "c" + "n": return "d" + } +end + +procedure message(s) + local x + x := 5 + EraseArea(x,height-fheight,width,fheight) + GotoXY(x,height-descent) + WWrites(s) + return +end + + +procedure hiddenNames(p) + local i, s, card + + i := up[p] + s := "" + every card := pile[p][i to *(pile[p])] do { + s ||:= rankName(card) || suitName(card) + } + return s +end + +procedure pileNames(p) + local i,run,s + + i := up[p] + s := "" + while ( i >= 1 ) do { + s ||:= rankName(pile[p][i]) + run := 0 + while ( pile[p][i] == succ(pile[p][i-1])[1] ) do { + i -:= 1 + run := 1 + } + if ( run = 1 ) then { + s ||:= "-" + s ||:= rankName(pile[p][i]) + } + s ||:= suitName(pile[p][i]) + i -:= 1 + } + return s +end + +procedure saveFile() + local output + + repeat { + case OpenDialog("Save game as:",currentFile) of { + "Okay": { + if output := open(dialog_value,"w") then { + currentFile := dialog_value + write(output,SPIDER_VERSION) + write(output,deck) + every writes(output,!undoStack," ") + write(output,"") + return + } else { + Notice("Cannot open file for writing.") + } + } + "Cancel" : fail + } + } +end + + +procedure readFile() + local input + + repeat { + case OpenDialog(,currentFile) of { + "Okay": { + if input := open(dialog_value) then { + currentFile := dialog_value + if read(input)==SPIDER_VERSION then { + newgame(read(input)) + read(input) ? { + while put(undoStack,integer(tab(upto(" ")))) + } + if doAll() then return + } + Notice("Not a valid spider game file.") + } else + Notice("Cannot open file.") + } + "Cancel": fail + } + } +end + +procedure doAll() + local i,doFlag,toPile,fromPile,n + +# writes(">") +# every writes(!undoStack," ") +# write("") + + i := *undoStack + while i >= 1 do { + case doFlag := undoStack[i-3] of { + 0 | 1: { + toPile := undoStack[i-2] + fromPile := undoStack[i-1] + n := undoStack[i] + up[fromPile] +:= doFlag # doFlag = 1 means turn card + moveNoUndo(n,fromPile,toPile) | fail + } + 2: { + deal() | fail + } + } + i -:= 4 + } + return +end diff --git a/ipl/gprogs/spiral.icn b/ipl/gprogs/spiral.icn new file mode 100644 index 0000000..6109b39 --- /dev/null +++ b/ipl/gprogs/spiral.icn @@ -0,0 +1,100 @@ +############################################################################ +# +# File: spiral.icn +# +# Subject: Program to draw polygonal spirals +# +# Author: Stephen B. Wampler +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.0 +# +############################################################################ +# +# +# Comments: This program displays polyline based spiral +# +# See the procedure 'helpmsg' for command line options +# (or run as 'spiral -help') +# +# Waits for a window event before closing window +# +############################################################################ +# +# Links: glib, wopen +# +############################################################################ +# +# Requires: Version 9 graphics and co-expressions (for glib.icn) +# +############################################################################ + +link glib +link wopen + +global win, mono, h, w +global Window, XMAX, YMAX + +procedure main (args) + local dist, angle, incr, n, nextarg, arg, t + + XMAX := YMAX := 700 # physical screen size + w := h := 1.0 + + dist := 0.02 + angle := 144 + incr := 0.01 + n := 100 + + nextarg := create !args + while arg := @nextarg do { + if arg == ("-help"|"-h") then stop(helpmsg()) + if match(arg, "-distance") then dist := numeric(@nextarg) + else if match(arg, "-angle") then angle := numeric(@nextarg) + else if match(arg, "-increment") then incr := numeric(@nextarg) + else if arg == "-n" then n := integer(@nextarg) + } + + win := WOpen("label=Poly Spiral", "width="||XMAX, "height="||YMAX) + mono := WAttrib (win, "depth") == "1" + Window := set_window(win, point(0,0), point(w,h), + viewport(point(0,0), point(XMAX, YMAX), win)) + + EraseArea(win) + + Fg(win, "black") + t := turtle(Window, point(w/2, h/2), 0) + polyspiral(t, dist, angle, incr, n) + + Event(win) + close(win) +end + +procedure polyspiral(t, dist, angle, incr, n) + local i + + every i := 1 to n do { + Line_Forward(t, dist) + Right(t, angle) + dist +:= incr + } + +end + +procedure helpmsg() + write("Usage: Spiral [-d dist] [-a angle] [-i increment] [-n nlines]") + write(" where") + write(" -d N -- initial line length {default: 0.02") + write(" -a N -- angle of change (degrees) {144}") + write(" -i N -- incremental change to line {0.01}") + write(" -n N -- number of lines to draw {100}") + return +end + diff --git a/ipl/gprogs/spiro.icn b/ipl/gprogs/spiro.icn new file mode 100644 index 0000000..42ac1b3 --- /dev/null +++ b/ipl/gprogs/spiro.icn @@ -0,0 +1,148 @@ +############################################################################ +# +# File: spiro.icn +# +# Subject: Program to display spirograph lines +# +# Author: Stephen B. Wampler +# +# Date: June 17, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.0 +# +############################################################################ +# +# +# Comments: This program displays spirograph-like output +# There are two methods of drawing: epitrochoid, where +# the secondary circle moves around the outside of the +# primary circle, and hypotrochoid (the default here), +# where the secondary circle moves around the inside of +# the primary circle. +# +# See the procedure 'helpmsg' for command line options +# (or run as 'spiro -help') +# +# Waits for a window event before closing window +# +############################################################################ +# +# Links: glib, wopen +# +############################################################################ +# +# Requires: Version 9 graphics and co-expressions (for glib.icn) +# +############################################################################ + +link glib # need the turtle graphic stuff +link wopen + +global win, mono, h, w +global Window, XMAX, YMAX + +procedure main (args) + local a, b, k, t1, t2, N, arg, use_epi, t, alist + + XMAX := YMAX := 700 # physical screen size + w := h := 350.0 + + a := 100.0 + b := 5.0 + k := 20.0 + t1 := 0.0 + t2 := 1.0 # only roll around once. + N := 500 + + while arg := get(args) do { + case arg of { + "-help"|"-h" : helpmsg() + "-epi" : use_epi := "yes" + "-a": a := real(get(args)) + "-b": b := real(get(args)) + "-k": k := real(get(args)) + "-t1": t1 := real(get(args)) + "-t2": t2 := real(get(args)) + "-N" : N := integer(get(args)) + } + } + + win := WOpen("label=Spirograph", "width="||XMAX, "height="||YMAX) + mono := WAttrib (win, "depth") == "1" + Window := set_window(win, point(-w,-h), point(w,h), + viewport(point(0,0), point(XMAX, YMAX), win)) + + EraseArea(win) + + t := turtle(Window, point(w/2, h/2), 0, create |"red") + + # build list of arguments to pass to parametric equations + # (same list for both x and y equations here) + alist := [a,b,k] + + if \use_epi then + draw_curve(t,epi_x,alist,epi_y,alist,t1,t2,N) + else + draw_curve(t,hypo_x,alist,hypo_y,alist,t1,t2,N) + + + # sit and wait for an event on the window. + Event(win) + close(win) +end + +procedure epi_x(t,a[]) + static twopi + local ab + initial twopi := 2*&pi + + ab := a[1]+a[2] + return (ab)*cos(twopi*t) - a[3]*cos(twopi*((ab)*t)/a[2]) +end + +procedure epi_y(t,a[]) + static twopi + local ab + initial twopi := 2*&pi + + ab := a[1]+a[2] + return (ab)*sin(twopi*t) - a[3]*sin(twopi*((ab)*t)/a[2]) +end + +procedure hypo_x(t,a[]) + static twopi + local ab + initial twopi := 2*&pi + + ab := a[1]-a[2] + return (ab)*cos(twopi*t) + a[3]*cos(twopi*((ab)*t)/a[2]) +end + +procedure hypo_y(t,a[]) + static twopi + local ab + initial twopi := 2*&pi + + ab := a[1]-a[2] + return (ab)*sin(twopi*t) - a[3]*sin(twopi*((ab)*t)/a[2]) +end + +procedure helpmsg() + write("Usage: Spiro [-a r] [-b r] [-k r] [-t1 r] [-t2 r] [-N n] [-epi]") + write() + write("where:") + write("\t-a r - radius of center circle {default 100}") + write("\t-b r - radius of moving circle {5}") + write("\t-k r - distance of pen from center of moving circle {20}") + write("\t-t1 r - initial value for parameter {0.0}") + write("\t-t2 r - final value for parameter {1.0 (one revolutio)}") + write("\t-N n - number of intervals to draw {500}") + write("\t-epi - use epitrochoid instead of hypotrochoid") + stop() +end diff --git a/ipl/gprogs/splat.icn b/ipl/gprogs/splat.icn new file mode 100644 index 0000000..1c85f0c --- /dev/null +++ b/ipl/gprogs/splat.icn @@ -0,0 +1,51 @@ +############################################################################ +# +# File: splat.icn +# +# Subject: Program to drop paint splatters in a window +# +# Author: Gregg M. Townsend +# +# Date: September 30, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# usage: splat [nspots [diameter]] +# +# splat draws random circular spots in a window. The number of spots +# and maximum diameter can be passed as command options. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, random +# +############################################################################ + +link graphics +link random + +procedure main(args) + local w, h, n, m, d + + Window("size=800,500", args) + w := WAttrib("width") + h := WAttrib("height") + n := integer(args[1]) | 1000 + m := integer(args[2]) | 100 + + randomize() + every 1 to n do { + Shade(RandomColor()) + d := (?m * ?m * ?m) / (m * m) + FillArc(?(w - d - 1), ?(h - d - 1), d, d) + } + WDone() +end diff --git a/ipl/gprogs/spokes.icn b/ipl/gprogs/spokes.icn new file mode 100644 index 0000000..e0c2c81 --- /dev/null +++ b/ipl/gprogs/spokes.icn @@ -0,0 +1,91 @@ +############################################################################ +# +# File: spokes.icn +# +# Subject: Program to draw spokes design +# +# Author: Ralph E. Griswold +# +# Date: September 13, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws "spokes" patterns. +# +# The following options are supported: +# +# -g run continuously; ignore user events; default: process user +# events +# -l i limit on number of iterations, default 2 ^ 10 +# -n i maximum number of spokes, default 50 +# -s i size of window (width/height); default 256 +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: interact, options, wopen +# +############################################################################ + +link interact +link options +link wopen + +procedure main(args) + local i, j, k, angle, incr, xpoint, ypoint, size, radius, opts + local extent, max, limit, run + + opts := options(args, "gl+n+s+") + + extent := \opts["s"] | 256 + limit := \opts["l"] | (2 ^ 10) + max := \opts["n"] | 50 + run := opts["g"] + + radius := extent / 4 + + WOpen("label=spokes", "width=" || extent, "height=" || extent, + "bg=light gray", "dx=" || (extent / 2), "dy=" || (extent / 2)) | + ExitNotice("Cannot open window.") + + every 1 to limit do { + i := ?max + if i < 4 then i+:= 3 + ?10 # too few doesn't work well ... + angle := 0.0 + incr := 2 * &pi / i + every j := 1 to i do { + spokes(radius * cos(angle), radius * sin(angle), radius, i, angle) + angle +:= incr + } + if /run then repeat case Event() of { + "q": exit() + "s": snapshot() + "n": break + } + WDelay(1000) + EraseArea() + } + +end + +procedure spokes(x, y, r, i, angle) + local incr, j + + incr := 2 * &pi / i + + every j := 1 to i do { + DrawLine(x, y, x + r * cos(angle), y + r * sin(angle)) + angle +:= incr + } + + return + +end + diff --git a/ipl/gprogs/striper.icn b/ipl/gprogs/striper.icn new file mode 100644 index 0000000..5cf4f07 --- /dev/null +++ b/ipl/gprogs/striper.icn @@ -0,0 +1,87 @@ +############################################################################ +# +# File: striper.icn +# +# Subject: Program to make striped pattern from image edge +# +# Author: Ralph E. Griswold +# +# Date: March 14, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes the left column or top row of pixels of an image +# and creates a 1 x n or n x 1 image file from it. The result, when +# tiled, is a striped pattern. +# +# This program is useful for creating regular striped patterns from +# scans. +# +# The following options are supported: +# +# -d s stripe direction: +# "h" horizontal (the default) +# "v" vertical +# "b" both horizontal and vertical +# -p s prefix for GIF file names, default "stripes_" +# -w i width of swatch, default 1 +# -x i x offset, default 0 +# -y i y offset, default 0 +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, wopen +# +############################################################################ + +link options +link wopen + +procedure main(args) + local file, hidden, count, prefix, opts, w, h, v, x, y + + opts := options(args, "d:w+p:x+y+") + prefix := \opts["p"] | "stripes_" + w := \opts["w"] | 1 + x := \opts["x"] | 0 + y := \opts["y"] | 0 + case opts["d"] of { + "h": h := 1 + "v": v := 1 + "b": { + h := 1 + v := 1 + } + &null: h := 1 + default: stop("Invalid direcTion specification") + } + + count := 0 + + every file := !args do { + hidden := WOpen("canvas=hidden", "image=" || file) | { + write(&errout, "*** cannot open ", file) + next + } + if \h then { + WriteImage(hidden, prefix || right(count +:= 1, 3, "0") || ".gif", + x, 0, w, WAttrib(hidden, "height")) | + write(&errout, "*** cannot write image file") + } + if \v then { + WriteImage(hidden, prefix || right(count +:= 1, 3, "0") || ".gif", + 0, y, WAttrib(hidden, "width"), w) | + write(&errout, "*** cannot write image file") + } + WClose(hidden) + } + +end diff --git a/ipl/gprogs/subdemo.icn b/ipl/gprogs/subdemo.icn new file mode 100644 index 0000000..881d888 --- /dev/null +++ b/ipl/gprogs/subdemo.icn @@ -0,0 +1,264 @@ +############################################################################ +# +# File: subdemo.icn +# +# Subject: Program to show the turtle graphics subset +# +# Author: Gregg M. Townsend +# +# Date: May 31, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# subdemo displays various random designs in a window using the +# turtle graphics subset library procedures. Click in the window, +# or enter a character on the keyboard, to start a new design. +# +# The following keyboard characters have meaning: +# +# w or W: random walk +# b or B: fractal bush (looks like "desert broom") +# s or S: spiral design +# p or P: polygon design +# t or T: rectangular tiling +# r or R: radial tiling +# +# \n, \r, \t, or SP: choose design randomly +# q or Q: exit program +# +# 0: pause drawing +# 1, ... 9: set speed of drawing (9 is fastest) +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, subturtl, random, graphics +# +############################################################################ + +link options +link optwindw +link subturtl +link random +link graphics + +global msec # delay between drawing actions +global event # interrupting event, if any + +procedure main(args) + local opts, dlist, p, e + + opts := options(args, winoptions()) + /opts["W"] := /opts["H"] := 500 + &window := optwindow(opts) + + randomize() + dlist := [walk, bush, poly, spiral, tile, radial] + msec := 0 + event := "\r" + repeat { + e := \event | Event() + event := &null + case e of { + QuitEvents(): break + "\n" | "\r" | "\t" | " ": run(?dlist) + &lrelease | &mrelease | &rrelease: run(?dlist) + "b" | "B": run(bush) + "w" | "W": run(walk) + "s" | "S": run(spiral) + "p" | "P": run(poly) + "t" | "T": run(tile) + "r" | "R": run(radial) + "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9": setdelay(e) + } + } +end + +# run(p) -- execute procedure p after resetting screen environment + +procedure run(p) + TReset() + return p() +end + +# continue() -- delay and check for interrupts +# +# Every demo should call this periodically and should exit if it fails. +# The global "event" is set to the interrupting event and can be checked +# to exit from recursive calls. + +procedure continue() + local evlist + + event := &null + delay(msec) + if *Pending() = 0 then + return + event := Event() + if setdelay(event) then { + event := &null + return + } + else + fail +end + +# setdelay(e) -- handle delay-setting event, or fail + +procedure setdelay(e) + while e === "0" do # 0 is pause -- wait until anything else input + e := Event() + if type(e) == "string" & *e = 1 & (e ? any(&digits)) then { + if e === "9" then + msec := 0 + else + msec := ishift(1, 12 - e) + return + } + else + fail +end + + +#################### drawing routines #################### + + +procedure walk() # random walk + local stepsize, maxturn, bias + + maxturn := 30 + bias := 1 + while continue() do + every 1 to 10 do { + TDraw(1) + TRight(?maxturn - maxturn/2.0 + bias) + } +end + + +procedure bush(n, len) # fractal bush + local maxturn + + if /n then { + TSkip(-150) + n := 4 + ?4 + len := 400 / n + } + maxturn := 60 + TSave() + TRight(?maxturn - maxturn / 2.0) + TDraw(?len) + if n > 0 & /event then { + continue() + every 1 to ?4 do + bush(n - 1, len) + } + TRestore() +end + + +procedure poly() # regular nonconvex polygon + local angle, side, x0, y0 + angle := 60 + ?119 + side := 200 - 100 * cos(dtor(angle)) + x0 := WAttrib("width") / 2 - side / 2 + y0 := WAttrib("height") / 2 - side / 3 + TGoto(x0, y0) + TLeft(THeading()) # set heading to zero (East) + while continue() do { + TDraw(side) + TRight(angle) + if abs(TX() - x0) + abs(TY() - y0) < 1 then break + } +end + + +procedure spiral() # polygon spiral + local angle, side, incr + angle := 30 + ?149 + incr := sqrt(4 * ?0) + 0.3 + side := 0 + while side < 1000 & continue() do { + TDraw(side +:= incr) + TRight(angle) + } +end + + +procedure tile() + local i, j, n, x0, y0, x, y, dx, dy, f, m + + n := 5 + x0 := WAttrib("width") / 2 + y0 := WAttrib("height") / 2 + dx := x0 / n + dy := y0 / n + f := mkfig(?10) + x := dx / 2 + m := dx + dy + every i := 1 to n do { + y := dy / 2 + every j := 1 to n do { + THeading(45) + TGoto(x0 + x, y0 + y); every 1 to 4 do { putfig(f, m); TRight(90) } + TGoto(x0 + x, y0 - y); every 1 to 4 do { putfig(f, m); TRight(90) } + TGoto(x0 - x, y0 + y); every 1 to 4 do { putfig(f, m); TRight(90) } + TGoto(x0 - x, y0 - y); every 1 to 4 do { putfig(f, m); TRight(90) } + y +:= dy + if not continue() then + return + } + x +:= dx + } +end + + +procedure radial() + local f, i, j, nrings, rwidth, fwd, circ, nfig, da + + f := mkfig(?8) + nrings := 5 + rwidth := WAttrib("width") / (2 * nrings) + every i := 1 to nrings do { + circ := &pi * 2 * i * rwidth + nfig := integer(circ / 50) + nfig := nfig / 2 + ?nfig + da := 360.0 / nfig + every j := 0 to nfig-1 do { + TGoto(WAttrib("width") / 2, WAttrib("height") / 2) + TRight(-THeading() + 90 - j * da) + TSkip(rwidth * (i - 0.9)) + putfig(f, rwidth) + if not continue() then + return + } + } +end + + +procedure mkfig(nseg) + local f + f := [] + every 1 to nseg do { + put(f, ?0 / nseg) # draw + put(f, -90 + 180 * ?0) # turn + } + return f +end + +procedure putfig(f, m) + local i + TSave() + every i := 1 to *f by 2 do { + TDraw(m * f[i]) + TRight(f[i+1]) + } + TRestore() +end diff --git a/ipl/gprogs/sym4mm.icn b/ipl/gprogs/sym4mm.icn new file mode 100644 index 0000000..717c539 --- /dev/null +++ b/ipl/gprogs/sym4mm.icn @@ -0,0 +1,250 @@ +############################################################################ +# +# File: sym4mm.icn +# +# Subject: Program to draw symmetrically +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program draws with the eight symmetries of the square - 4mm +# symmetry. +# +# It is based on a simple drawing program by Gregg Townsend. +# +# Pressing the left mouse button draws a point. Dragging with the left mouse +# button depressed draws a line. Pressing and dragging with the middle mouse +# depressed shows a dashed straight line, which is drawn solid when +# the middle mouse button is released. Dragging with the right mouse +# button depressed erases in the vicinity of the mouse pointer. +# +# Typing "f" toggles restriction of drawing to the "generating region" +# which is shaded when drawing is restricted. +# +# Typing "g" toggles the grid lines. +# +# Typing "p" toggles the background in the generating region. +# +# Typing "c" clears the window. +# +# Typing "s" takes a snapshot, writing a GIF file. File names begin with +# a prefix, followed by three digits starting at 000 and increasing, and +# terminated by .GIF. +# +# Typing "q" terminates the session. +# +# Grid lines and shading are only provided for servers that support mutable +# colors. +# +# The options supported are: +# +# -w i width of the window, default 512 +# -h i height of the window, default 512 +# -s i size of square, default 512x512; supersedes -w and -h +# -p s prefix for image files, default "sym" +# +# Note: Although the window does not have to be square, the application is +# designed to work with a square window. +# +############################################################################ +# +# Requires: Graphics +# +############################################################################ +# +# Links: options, xio +# +############################################################################ + +link options +link xio + +procedure main(args) + local x, y, opts, number, w, h, prefix, curr, alt, restrict, xc, yc, grid + local xd, yd, nonrestrict, palt, pattern, pcurr, x1, y1, x2, y2, delta + + opts := options(args, "w+h+s+p:") + + number := -1 + + w := \opts["w"] | 512 + h := \opts["h"] | 512 + w := h := \opts["s"] + prefix := \opts["p"] | "sym" + + restrict := 1 # initially restricted + nonrestrict := &null + + WOpen("size=" || w || "," || h) | stop("*** cannot open window") + + xc := w / 2 + yc := h / 2 + + w -:= 1 # adjustment for 0-origin indexing + h -:= 1 + + curr := "light blue" + pcurr := "pink" + alt := "white" + palt := "white" + + Pattern("2,#01") + + if grid := NewColor(curr) then { + drawgrid(w, h, grid) + } + + if pattern := NewColor(pcurr) then { + shade(w, h, pattern) + } + + repeat case Event() of { + "f": { + restrict :=: nonrestrict + Color(\pattern, pcurr :=: palt) + } + "q": { + exit() + } + "c": { + EraseArea() + if \grid then { + drawgrid(w, h, grid) + shade(w, h, pattern) + } + } + "s": { + Color(\grid, "white") + Color(\pattern, "white") + WriteImage(prefix || right(number +:= 1, 3, 0) || ".gif") + Color(\grid, curr) + Color(\pattern, pcurr) + } + "g": { + Color(\grid, curr :=: alt) + } + "p": { + Color(\pattern, pcurr :=: palt) + } + &lpress: { + if \restrict & ((real(&x) / (&y + 0.0001) < 1.0) | (&x > xc) | + (&y > yc)) then next + every DrawPoint(&x | (w - &x), &y | (h - &y)) + every DrawPoint(&y | (w - &y), &x | (h - &x)) + x := &x + y := &y + } + &ldrag: { + if \x then { # just in case (for artificial events) + if \restrict & ((real(x) / (y + 0.0001) < 1.0) | (x > xc) | + (y > yc)) then next + DrawLine(x, y, &x, &y) + DrawLine(w - x, y, w - &x, &y) + DrawLine(x, h - y, &x, h - &y) + DrawLine(w - x, h - y, w - &x, h - &y) + DrawLine(y, x, &y, &x) + DrawLine(w - y, x, w - &y, &x) + DrawLine(y, h - x, &y, h - &x) + DrawLine(w - y, h - x, w - &y, h - &x) + } + x := &x + y := &y + } + &lrelease: { + x := y := &null + } + &mpress: { + x1 := xd := &x + y1 := yd := &y + WAttrib("linestyle=dashed") + WAttrib("drawop=reverse") + DrawLine(x1, y1, xd, yd) # start trace line + } + &mdrag: { + DrawLine(x1, y1, xd, yd) # erase current trace line + xd := &x + yd := &y + DrawLine(x1, y1, xd, yd) # draw new trace line + } + &mrelease: { + DrawLine(x1, y1, xd, yd) # erase trace line + WAttrib("drawop=copy") + WAttrib("linestyle=solid") + x2 := &x + y2 := &y + if \restrict then { # adjust end points + if ((x1 > xc) & (x2 > xc)) | ((y1 > yc) & (y2 > yc)) then next + if x2 > x1 then { + x1 :=: x2 + y1 :=: y2 + } + if x1 > xc then { + y1 := y2 + ((xc - x2) * (y1 - y2)) / (x1 - x2) + x1 := xc + } + if y2 > yc then { + x2 := x1 - ((x1 - x2) * (y1 - yc)) / (y1 - y2) + y2 := yc + } + if y1 > y2 then { + y1 :=: y2 + x1 :=: x2 + } + if y1 > x1 then next + if y2 > x2 then { + delta := real(x2 - x1) / (y2 - y1) + x2 := (x1 - y1 * delta) / (1 - delta) + y2 := x2 + } + } + DrawLine(x1, y1, x2, y2) + DrawLine(w - x1, y1, w - x2, y2) + DrawLine(x1, h - y1, x2, h - y2) + DrawLine(w - x1, h - y1, w - x2, h - y2) + DrawLine(y1, x1, y2, x2) + DrawLine(w - y1, x1, w - y2, x2) + DrawLine(y1, h - x1, y2, h - x2) + DrawLine(w - y1, h - x1, w - y2, h - x2) + x := &x + y := &y + } + &rpress | &rdrag: { + every EraseArea((&x - 2) | (w - &x - 2), + (&y - 2) | (h - &y - 2), 5, 5) + every EraseArea((&y - 2) | (w - &y - 2), + (&x - 2) | (h - &x - 2), 5, 5) + } + } +end + +procedure drawgrid(w, h, grid) + + Fg(grid) + DrawLine(0, 0, w, h) + DrawLine(w, 0, 0, h) + DrawLine(0, h / 2, w, h / 2) + DrawLine(w / 2, 0, w / 2, h) + Fg("bleck") + + return + +end + +procedure shade(w, h, pattern) + + Fg(pattern) + WAttrib("fillstyle=textured") + FillPolygon(1, 0, w / 2, 1, w / 2, h / 2, 1, 0) + WAttrib("fillstyle=solid") + Fg("black") + + return + +end diff --git a/ipl/gprogs/symdraw.icn b/ipl/gprogs/symdraw.icn new file mode 100644 index 0000000..ecc56a9 --- /dev/null +++ b/ipl/gprogs/symdraw.icn @@ -0,0 +1,338 @@ +############################################################################ +# +# File: symdraw.icn +# +# Subject: Program to draw symmetrically +# +# Author: Ralph E. Griswold +# +# Date: November 21, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Pressing the left mouse button draws a point. Dragging with the left mouse +# button draws a line. Pressing and dragging with the middle mouse +# shows a dashed straight line, which is drawn solid when +# the middle mouse button is released. Dragging with the right mouse +# button erases in the vicinity of the mouse pointer. +# +# There are several known bugs: +# +# Erasing in restricted mode is bogus outside the generating region. +# +# Perfectly vertical and horizontal straight lines are not clipped. +# +# Some legal straight lines are not drawn. +# +# In other words, the clipping logic is not correct. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, interact, vsetup +# +############################################################################ + +link graphics +link interact +link vsetup + +global W, H, X, Y, xc, yc, restrict, nonrestrict, gcurr, pcurr, galt, palt +global number, xd, yd, pattern, x1, y1, x2, y2, delta, x, y, lines, Pane + +procedure main(args) + local pane, vidgets, obg + + number := -1 + + vidgets := ui() + + VSet(vidgets["lines"], 1) # Start with lines, + VSet(vidgets["shade"], 1) # shading, + VSet(vidgets["restrict"], 1) # and restricted drawing enabled. + + pane := vidgets["pane"] + + W := pane.uw + H := pane.uh + X := pane.ux + Y := pane.uy + + Pane := Clone("bg=white", "dx=" || X, "dy=" || Y) + Clip(Pane, 0, 0, W, H) + + restrict := 1 # initially restricted + nonrestrict := &null + + xc := W / 2 + yc := H / 2 + + W -:= 1 # adjustment for 0-origin indexing + H -:= 1 + + gcurr := "light blue" + pcurr := "pink" + galt := "white" + palt := "white" + + Pattern(Pane, "2,#01") # pattern for shading generation region + + obg := Bg(Pane) + Bg(Pane, "white") +# EraseArea(Pane, 0, 0, W, H) + EraseArea(Pane) + Bg(Pane, obg) + + if lines := NewColor(Pane, gcurr) then { # requires mutable colors + drawlines() + } + + if pattern := NewColor(Pane, pcurr) then { # requires mutable colors + shade() + } + GetEvents(vidgets["root"], shortcuts) + +end + +procedure file_cb(vidget, value) + + case value[1] of { + "save @S": save() + "help @H": help() + "quit @Q": exit() + } + + fail + +end # not handled + +procedure pane_cb(vidget, event) # handle drawing events + local obg + + &x -:= X + &y -:= Y + + case event of { + &lpress: { # start free-hand drawing + if \restrict & ((real(&x) / (&y + 0.0001) < 1.0) | (&x > xc) | + (&y > yc)) then fail + every DrawPoint(Pane, &x | (W - &x), &y | (H - &y)) + every DrawPoint(Pane, &y | (W - &y), &x | (H - &x)) + x := &x + y := &y + } + &ldrag: { # free-hand drawing + if \x then { # just in case (for artificial events) + if \restrict & ((real(x) / (y + 0.0001) < 1.0) | (x > xc) | + (y > yc)) then fail + DrawLine(Pane, x, y, &x, &y) + DrawLine(Pane, W - x, y, W - &x, &y) + DrawLine(Pane, x, H - y, &x, H - &y) + DrawLine(Pane, W - x, H - y, W - &x, H - &y) + DrawLine(Pane, y, x, &y, &x) + DrawLine(Pane, W - y, x, W - &y, &x) + DrawLine(Pane, y, H - x, &y, H - &x) + DrawLine(Pane, W - y, H - x, W - &y, H - &x) + } + x := &x + y := &y + } + &lrelease: { # end free-hand drawing + x := y := &null + } + &mpress: { # start straight line + x1 := xd := &x + y1 := yd := &y + WAttrib(Pane, "linestyle=dashed") + WAttrib(Pane, "drawop=reverse") + DrawLine(Pane, x1, y1, xd, yd) # start trace line + } + &mdrag: { # locate end of straight line + DrawLine(Pane, x1, y1, xd, yd) # erase current trace + xd := &x + yd := &y + DrawLine(Pane, x1, y1, xd, yd) # draw new trace line + } + &mrelease: { # end straight line + DrawLine(Pane, x1, y1, xd, yd) # erase trace line + WAttrib(Pane, "drawop=copy") + WAttrib(Pane, "linestyle=solid") + x2 := &x + y2 := &y + + # This probably can be done in a better way. What's here "just grew" + + if \restrict then { # adjust end points + if ((x1 > xc) & (x2 > xc)) | ((y1 > yc) & (y2 > yc)) then fail + if x2 > x1 then { + x1 :=: x2 + y1 :=: y2 + } + if x1 > xc * x1 ~= x2 then { + y1 := y2 + ((xc - x2) * (y1 - y2)) / (x1 - x2) + x1 := xc + } + if y2 > yc & y1 ~= y2 then { + x2 := x1 - ((x1 - x2) * (y1 - yc)) / (y1 - y2) + y2 := yc + } + if y1 > y2 then { + y1 :=: y2 + x1 :=: x2 + } + if y1 > x1 then fail + if y2 > x2 & y1 ~= y2 then { + delta := real(x2 - x1) / (y2 - y1) + x2 := (x1 - y1 * delta) / (1 - delta) + y2 := x2 + } + } + DrawLine(Pane, x1, y1, x2, y2) + DrawLine(Pane, W - x1, y1, W - x2, y2) + DrawLine(Pane, x1, H - y1, x2, H - y2) + DrawLine(Pane, W - x1, H - y1, W - x2, H - y2) + DrawLine(Pane, y1, x1, y2, x2) + DrawLine(Pane, W - y1, x1, W - y2, x2) + DrawLine(Pane, y1, H - x1, y2, H - x2) + DrawLine(Pane, W - y1, H - x1, W - y2, H - x2) + x := &x + y := &y + } + + # This code is not correct when pointer is outside + # the generation region. + + &rpress | &rdrag: { # erase around pointer + obg := Bg(Pane) + Bg(Pane, "white") + every EraseArea(Pane, ((&x - 2) | (W - &x - 2)), + ((&y - 2) | (H - &y - 2)), 5, 5) + every EraseArea(Pane, ((&y - 2) | (W - &y - 2)), + ((&x - 2) | (H - &x - 2)), 5, 5) + Bg(Pane, obg) + } + } +end + +procedure help() # help (someday) + + Notice("There is no help to be had") + +end + +procedure shortcuts(event) + + if &meta & event := string(event) then + case map(event) of { # fold case + "q": exit() + "h": help() + "s": save() + } + + return + +end + +procedure lines_cb() # toggle lines + + Color(Pane, \lines, gcurr :=: galt) + +end + +procedure clear_cb() # clear drawing area + local obg + + obg := Bg(Pane) + Bg(Pane, "white") + EraseArea(Pane, 0, 0, W, H) + Bg(Pane, obg) + if \lines then { + drawlines() + shade() + } + +end + +procedure drawlines() # draw lines + local ofg, obg + + ofg := Fg(Pane) + obg := Bg(Pane) + Fg(Pane, lines) + Bg(Pane, "white") + DrawLine(Pane, 0, 0, W, H) + DrawLine(Pane, W, 0, 0, H) + DrawLine(Pane, 0, H / 2, W, H / 2) + DrawLine(Pane, W / 2, 0, W / 2, H) + Fg(Pane, ofg) + Bg(Pane, obg) + + return + +end + +procedure shade() # shade generating region + local ofg, obg + + ofg := Fg(Pane) + obg := Bg(Pane) + Fg(Pane, pattern) + Bg(Pane, "white") + WAttrib(Pane, "fillstyle=textured") + FillPolygon(Pane, 1, 0, W / 2, 1, W / 2, H / 2, 1, 0) + WAttrib(Pane, "fillstyle=solid") + Fg(Pane, ofg) + Bg(Pane, obg) + + return + +end + +procedure save() # save drawing in image file + + Color(Pane, \lines, "white") + Color(Pane, \pattern, "white") + snapshot(Pane, 0, 0, W, H) + Color(Pane, \lines, gcurr) + Color(Pane, \pattern, pcurr) + +end + +procedure restrict_cb() # toggle restriction to generating + # region + restrict :=: nonrestrict + +end + +procedure shade_cb() # toggle shading of generating region + + Color(Pane, \pattern, pcurr :=: palt) + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=523,461", "bg=pale-gray", "label=symdraw"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + ["symdraw:Sizer:::0,0,523,461:symdraw",], + ["clear:Button:regular::20,45,64,20:clear",clear_cb], + ["file:Menu:pull::33,4,36,21:File",file_cb, + ["save @S","help @H","quit @Q"]], + ["line:Line:::0,30,528,30:",], + ["lines:Button:regular:1:20,84,64,20:lines",lines_cb], + ["restrict:Button:regular:1:20,165,64,20:restrict",restrict_cb], + ["shade:Button:regular:1:20,125,64,20:shade",shade_cb], + ["pane:Rect:grooved::105,45,405,405:",pane_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/sympmm.icn b/ipl/gprogs/sympmm.icn new file mode 100644 index 0000000..e61b092 --- /dev/null +++ b/ipl/gprogs/sympmm.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: sympmm.icn +# +# Subject: Program to produce pmm symmetry composite images +# +# Author: Ralph E. Griswold +# +# Date: February 4, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program reflects and concatenates images in the horizontal and +# vertical directions to produce composite images with the pmm ("prickly +# pear") plane symmetry. The resulting images tile seamlessly. +# +# The composite images are given the base name of the input images with +# "_pmm" appended. +# +# Warning: This program is slow. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, wopen, xformimg +# +############################################################################ + +link basename +link xformimg +link wopen + +procedure main(args) + local name, base, win1, win2, win3, win4, win5 + + every name := !args do { + base := basename(name, ".gif") + win1 := WOpen("canvas=hidden", "image=" || name) | { + write(&errout, "*** cannot open ", name) + next + } + win2 := wreflect(win1, "v") + win3 := wcatenate(win1, win2, "h") + WClose(win1) + WClose(win2) + win4 := wreflect(win3, "h") + win5 := wcatenate(win3, win4, "v") + WClose(win3) + WClose(win4) + WriteImage(win5, base || "_pmm.gif") + WClose(win5) + } + +end + diff --git a/ipl/gprogs/testpatt.icn b/ipl/gprogs/testpatt.icn new file mode 100644 index 0000000..8188e8c --- /dev/null +++ b/ipl/gprogs/testpatt.icn @@ -0,0 +1,199 @@ +############################################################################ +# +# File: testpatt.icn +# +# Subject: Program to show test patterns +# +# Author: Gregg M. Townsend +# +# Date: July 18, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# testpatt cycles through a set of test patterns as the return +# key is pressed. Backspacing cycles in the other direction. +# The window can be resized at any time. Press "q" to exit. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, imscolor +# +############################################################################ + +link graphics +link imscolor + +global wwidth, wheight # window width and height + +$define SQUARE 60 # size of squares, in pixels + + +# main procedure + +procedure main(args) + local patlist + + Window("size=1000,700", "gamma=1.0", args) + &error := 1 + WAttrib("resize=on") + &error := 0 + + patlist := [checkers, grid, ghost, pinstripe, white, bars, palette] + # revolving list of procedures + + reset() # reset display + patlist[1]() # display initial pattern + + repeat case Event() of { + !"\r\n": { # \r or \n advances pattern + put(patlist, get(patlist)) + reset() + patlist[1]() + } + !"\b\d": { # \b or \d cycles backwards + push(patlist, pull(patlist)) + reset() + patlist[1]() + } + !"qQ": # q exits the program + exit() + &resize: { # resize requires redrawing + reset() + patlist[1]() + } + } +end + + +# reset() -- prepare for next test +# +# The screen is cleared and the fg/bg colors are reset. +# Each test procedure is responsible for restoring anything else it changes. + +procedure reset() + WAttrib("fg=black", "bg=white") + EraseArea() + wwidth := WAttrib("width") + wheight := WAttrib("height") + return +end + + +# checkers() -- a checkerboard with additional lines +# +# There should be no red or green tinge to the edges of the squares. + +procedure checkers() + local x, y + + WAttrib("drawop=reverse") + every x := 0 to wwidth by 2 * SQUARE do { + FillRectangle(x, 0, SQUARE, wheight) + DrawLine(x + SQUARE / 2, 0, x + SQUARE / 2, wheight) + } + every y := 0 to wheight by 2 * SQUARE do { + FillRectangle(0, y, wwidth, SQUARE) + DrawLine(0, y + SQUARE / 2, wwidth, y + SQUARE / 2) + } + WAttrib("drawop=copy") + return +end + + +# grid() -- a grid of white lines + +procedure grid() + local x, y + + FillRectangle() + Fg("white") + every x := SQUARE / 2 to wwidth by SQUARE do + DrawLine(x, 0, x, wheight) + every y := SQUARE / 2 to wheight by SQUARE do + DrawLine(0, y, wwidth, y) + return +end + + +# ghost() -- generate ghosting pattern +# +# Look for white echoes of the black vertical lines +# displaced about 1mm to their right. + +procedure ghost() + $define NSTEPS 12 + local dx, x1, x2, y1, y2, i, g + + dx := wwidth / NSTEPS + x1 := .10 * dx + x2 := .90 * dx + y1 := .95 * wheight + y2 := .05 * wheight + every i := 1 to NSTEPS do { + g := i * 65535 / NSTEPS + Bg(g || "," || g || "," || g) + WAttrib("dx=" || integer((i - 1) * dx)) + EraseArea(0, 0, dx + 1, wheight) + DrawLine(x1, y1, x1, y2, x2, y1)#, x2, y2) + } + WAttrib("bg=white", "dx=0") + return +end + + +# pinstripe() -- generate vertical pinstripe pattern +# +# The moire patterns that result on a Trinitron-type CRT +# reveal the consistency of pixel sizing across the display. + +procedure pinstripe() + WAttrib("pattern=2,#2", "fillstyle=textured") + FillRectangle() + WAttrib("fillstyle=solid") + return +end + + +# white() -- generate a white screen + +procedure white() + return +end + + +# bars() -- generate color bars + +procedure bars() + local dx, i + + dx := (wwidth + 7) / 8 + "black blue red magenta green cyan yellow white " ? + every i := 0 to 7 do { + Fg(tab(upto(' '))) + move(1) + FillRectangle(i * dx, 0, dx, wheight) + } + return +end + + +# palette() -- draw color palettes + +procedure palette() + local dx + + dx := (wwidth + 3) / 4 + drawpalette("c1", 0, 0, dx, wheight, "") + drawpalette("c1", dx, 0, dx, wheight, "l") + drawpalette("c1", 2 * dx, 0, dx, wheight, "o") + drawpalette("c1", 3 * dx, 0, dx, wheight, "") + return +end diff --git a/ipl/gprogs/textures.icn b/ipl/gprogs/textures.icn new file mode 100644 index 0000000..3069650 --- /dev/null +++ b/ipl/gprogs/textures.icn @@ -0,0 +1,86 @@ +############################################################################ +# +# File: textures.icn +# +# Subject: Program to show various 4x4 patterns +# +# Author: Gregg M. Townsend +# +# Date: May 31, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# textures illustrates many different patterns that can be +# created by tiling a 4x4 pixel cell. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics +# +############################################################################ + +link graphics + +global win + +procedure main(args) + local cols, rows, xsiz, ysiz, gutter, w, h, pats, i, x, y, s + + pats := [ + "#0000 #0010 #8010 #0820 #0420 #1040", + "#8050 #0124 #0424 #0260 #0142 #0610 #0224 #0601 #2208", + "#A050 #0161 #1414 #0660 #1284 #4221 #0168 #1144 #0505 _ + #0258 #0158 #8421 #4510 #0306", + "#A052 #8641 #8443 #1922 #0272 #0525 #0515 #0433 #281C", + "#A452 #0356 #2C34 #2A54 #1C32 #8711 #88E1 #0555 #0707 #070D #5451", + "#A552 #8356 #2F22 #2555 #0787 #5A1A #124F #121F #9887", + "#6666 #5555 #5AA5 #A5A5 #9696 #0F0F #0FF0"] + + cols := 2 * *pats - 1 + rows := 16 + xsiz := 36 + ysiz := 30 + gutter := 6 + + w := cols * xsiz + (cols + 1) * gutter - 1 + h := rows * ysiz + (rows + 1) * gutter - 1 + win := open("textures", "g", "width="||w, "height="||h) + + Shade(win, "gray") + FillRectangle(win, 0, 0, w, h) + Fg(win, "black") + + WAttrib(win, "fillstyle=textured") + + every i := 1 to *pats do { + y := gutter + x := gutter + 2 * (xsiz + gutter) * (i - 1) + pats[i] ? { + while tab(upto('#')) do { + s := move(5) + rect(x, y, xsiz, ysiz, s) + rect(x + xsiz + gutter, y, xsiz, ysiz, + map(s, "0123456789ABCDEF", "FEDCBA9876543210")) + y +:= ysiz + gutter + } + } + } + WDone(win) +end + +procedure rect(x, y, w, h, s) + Pattern(win, "1,1") + DrawLine(win, x + w, y - 1, x + w, y + h, x - 1, y + h) + Pattern(win, "1,0") + DrawLine(win, x - 1, y + h, x - 1, y - 1, x + w, y - 1) + Pattern(win, "4," || s) + FillRectangle(win, x, y, w, h) +end diff --git a/ipl/gprogs/tgdemo.icn b/ipl/gprogs/tgdemo.icn new file mode 100644 index 0000000..d6c66fb --- /dev/null +++ b/ipl/gprogs/tgdemo.icn @@ -0,0 +1,263 @@ +############################################################################ +# +# File: tgdemo.icn +# +# Subject: Program to demonstrate turtle graphics +# +# Author: Gregg M. Townsend +# +# Date: May 31, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# tgdemo displays various random designs in a window using the +# turtle graphics library procedures. Click in the window, or +# enter a character on the keyboard, to start a new design. +# +# The following keyboard characters have meaning: +# +# w or W: random walk +# b or B: fractal bush (looks like "desert broom") +# s or S: spiral design +# p or P: polygon design +# t or T: rectangular tiling +# r or R: radial tiling +# +# \n, \r, \t, or SP: choose design randomly +# q or Q: exit program +# +# 0: pause drawing +# 1, ... 9: set speed of drawing (9 is fastest) +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, turtle, random, graphics +# +############################################################################ + +link options +link optwindw +link turtle +link random +link graphics + +global msec # delay between drawing actions +global event # interrupting event, if any + +procedure main(args) + local opts, dlist, p, e + + opts := options(args, winoptions()) + /opts["W"] := /opts["H"] := 500 + &window := optwindow(opts) + + randomize() + dlist := [walk, bush, poly, spiral, tile, radial] + msec := 0 + event := "\r" + repeat { + e := \event | Event() + event := &null + case e of { + QuitEvents(): break + "\n" | "\r" | "\t" | " ": run(?dlist) + &lrelease | &mrelease | &rrelease: run(?dlist) + "b" | "B": run(bush) + "w" | "W": run(walk) + "s" | "S": run(spiral) + "p" | "P": run(poly) + "t" | "T": run(tile) + "r" | "R": run(radial) + "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9": setdelay(e) + } + } +end + +# run(p) -- execute procedure p after resetting screen environment + +procedure run(p) + TReset() + return p() +end + +# continue() -- delay and check for interrupts +# +# Every demo should call this periodically and should exit if it fails. +# The global "event" is set to the interrupting event and can be checked +# to exit from recursive calls. + +procedure continue() + local evlist + + event := &null + delay(msec) + if *Pending() = 0 then + return + event := Event() + if setdelay(event) then { + event := &null + return + } + else + fail +end + +# setdelay(e) -- handle delay-setting event, or fail + +procedure setdelay(e) + while e === "0" do # 0 is pause -- wait until anything else input + e := Event() + if type(e) == "string" & *e = 1 & (e ? any(&digits)) then { + if e === "9" then + msec := 0 + else + msec := ishift(1, 12 - e) + return + } + else + fail +end + + +#################### drawing routines #################### + + +procedure walk() # random walk + local stepsize, maxturn, bias + + maxturn := 30 + bias := 1 + while continue() do + every 1 to 10 do { + TDraw(1) + TRight(?maxturn - maxturn/2.0 + bias) + } +end + + +procedure bush(n, len) # fractal bush + local maxturn + + if /n then { + TSkip(-150) + n := 4 + ?4 + len := 400 / n + } + maxturn := 60 + TSave() + TRight(?maxturn - maxturn / 2.0) + TDraw(?len) + if n > 0 & /event then { + continue() + every 1 to ?4 do + bush(n - 1, len) + } + TRestore() +end + + +procedure poly() # regular nonconvex polygon + local angle, side, x0, y0 + angle := 60 + ?119 + side := 200 - 100 * cos(dtor(angle)) + x0 := WAttrib("width") / 2 - side / 2 + y0 := WAttrib("height") / 2 - side / 3 + TGoto(x0, y0, 0) + while continue() do { + TDraw(side) + TRight(angle) + if abs(TX() - x0) + abs(TY() - y0) < 1 then break + } +end + + +procedure spiral() # polygon spiral + local angle, side, incr + angle := 30 + ?149 + incr := sqrt(4 * ?0) + 0.3 + side := 0 + while side < 1000 & continue() do { + TDraw(side +:= incr) + TRight(angle) + } +end + + +procedure tile() + local i, j, n, x0, y0, x, y, dx, dy, f + + n := 5 + x0 := WAttrib("width") / 2 + y0 := WAttrib("height") / 2 + dx := x0 / n + dy := y0 / n + f := mkfig(?10) + x := dx / 2 + TScale(dx + dy) + every i := 1 to n do { + y := dy / 2 + every j := 1 to n do { + THeading(45) + TGoto(x0 + x, y0 + y); every 1 to 4 do { putfig(f); TRight(90) } + TGoto(x0 + x, y0 - y); every 1 to 4 do { putfig(f); TRight(90) } + TGoto(x0 - x, y0 + y); every 1 to 4 do { putfig(f); TRight(90) } + TGoto(x0 - x, y0 - y); every 1 to 4 do { putfig(f); TRight(90) } + y +:= dy + if not continue() then + return + } + x +:= dx + } +end + + +procedure radial() + local f, i, j, nrings, rwidth, fwd, circ, nfig, da + + f := mkfig(?8) + nrings := 5 + rwidth := WAttrib("width") / (2 * nrings) + TScale(rwidth) + every i := 1 to nrings do { + circ := &pi * 2 * i * rwidth + nfig := integer(circ / 50) + nfig := nfig / 2 + ?nfig + da := 360.0 / nfig + every j := 0 to nfig-1 do { + TGoto(, , 90 - j * da) + TSkip(i - 0.9) + putfig(f) + if not continue() then + return + } + } +end + + +procedure mkfig(nseg) + local f + f := [] + every 1 to nseg do { + put(f, ?0 / nseg) # draw + put(f, -90 + 180 * ?0) # turn + } + return f +end + +procedure putfig(f) + local i + TSave() + every i := 1 to *f by 2 do { + TDraw(f[i]) + TRight(f[i+1]) + } + TRestore() +end diff --git a/ipl/gprogs/tilescan.icn b/ipl/gprogs/tilescan.icn new file mode 100644 index 0000000..395d000 --- /dev/null +++ b/ipl/gprogs/tilescan.icn @@ -0,0 +1,649 @@ +############################################################################ +# +# File: tilescan.icn +# +# Subject: Program to select tile from an image +# +# Author: Ralph E. Griswold +# +# Date: December 14, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program is designed to assist in locating areas within an image +# that, when tiled, produce a desired effect. For example, a background +# may consist of a tiled image; this program can be used to find the +# smallest tile for the repeat (by "eye-balling"). It's worth noting +# that interesting images can be found for other settings. For example, +# another interesting use of this program is to produce striped patterns by +# selecting a row or column of an image to get a tile that is one character +# wide. Sometimes a few rows or columns give an interesting "fabric" +# effect. +# +# There are three windows: +# +# the VIB control window +# the source image window +# a repeat window, which shows the selection from the source +# image, tiled. +# +# The selection from the source image is shown as a marquee in the +# source image window. When a source image is loaded, the marquee starts +# with the entire image. The marquee can be changed by buttons and +# arrow-key events on the control window (not the source image window). +# +# The arrow keys have two modes. With no modifier, they nudge the +# location of the marquee. With the meta-key modifier, they nudge +# the dimensions of the marquee. +# +# The reset button resets the marquee to the entire image. +# +# The current selection can be mirrored using the mirror button. +# +# The following features are provided through keyboard shortcuts, +# the File menu, and in some cases, on-board buttons: +# +# @M mirror selection +# @M mirror selection +# @O open new source image +# @P pick a source image from GIF files in the current directory +# @Q quit application +# @S save current selection as an image +# +# The repeat window can be resized by the user, but it is not redrawn +# until the marque is changed or the refresh button is pushed. +# +############################################################################ +# +# Requires: Version 9 graphics, UNIX (for "pick" feature) +# +############################################################################ +# +# Links: grecords, interact, mirror, tile +# +############################################################################ +# +# Includes: keysyms.icn +# +############################################################################ + +link grecords +link interact +link mirror +link tile + +$include "keysyms.icn" + +# Globals related to windows: + +global controls # VIB control window +global pattern # repeat window +global screen # source image window visible +global source # source image window hidden +global symmetry # mirroring window + +global posx # x position relative to interface window +global posy # y position relative to repeat window + +global sx # marquee location information +global sy + +# Globals related to the selection: + +global current # current selection record +global hmax # maximum height of source image +global wmax # maximum width of source image +global previous # previous selection record + +global vidgets # table of interface vidgets + +procedure main() + local atts, x1, y1 + + atts := ui_atts() + put(atts, "posx=10", "posy=10") + + controls := (WOpen ! atts) | ExitNotice("Cannot open control window.") + + vidgets := ui() + + init() + + repeat { + while *Pending(controls) > 0 do + ProcessEvent(vidgets["root"], , shortcuts) + while *Pending(\screen) > 0 do + if Event(screen) === &lpress then draw_marquee() + } + +end + +# Callback that handles all the buttons that change x, y, w, and h. + +procedure dimens_cb(vidget, value) + + if /source then fail + + # Cute code alert: The selected reversible assignment is performed + # and passed to check(). It checks the resulting selection rectangle + # and fails if it's not valid. That failure causes the reversible + # assignment to be undone and the expression fails, leaving the + # selection as it was. + + case value of { + "w max": current.w := (wmax - current.x) + "h max": current.h := (hmax - current.y) + "w = 1": current.w := 1 + "h = 1": current.h := 1 + "full": { + current.h := hmax + current.w := wmax + current.x := 0 + current.y := 0 + } + "w / 2": check(current.w <- current.w / 2) + "h / 2": check(current.h <- current.h / 2) + "w * 2": check(current.w <- current.w * 2) + "h * 2": check(current.h <- current.h * 2) + } | fail + + show() + + return + +end + +procedure draw_marquee() + local x1, y1 + + current.x := &x + current.y := &y + current.h := current.w := 0 + + update() + + repeat { + case Event(screen) of { + &ldrag: update_marquee() + &lrelease: { + update_marquee() + Raise(controls) + return + } + } + } + +end + +procedure update_marquee() + + if &x < 0 then &x := 0 + if &y < 0 then &y := 0 + if &x > wmax then &x := wmax + if &y > hmax then &y := hmax + current.w := &x - current.x + current.h := &y - current.y + + show() + + return + +end + +procedure location_cb(vidget, value) + + if /source then fail + + # Cute code alert: The selected reversible assignment is performed + # and passed to check(). It checks the resulting selection rectangle + # and fails if it's not valid. That failure causes the reversible + # assignment to be undone and the expression fails, leaving the + # selection as it was. + + case value of { + "nw": current.x := current.y := 0 + "ne": { + current.x := wmax - current.w + current.y := 0 + } + "se": { + current.x := wmax - current.w + current.y := hmax - current.h + } + "sw": { + current.x := 0 + current.y := hmax - current.h + } + "x max": current.x := wmax - current.w + "y max": current.y := hmax - current.h + "center": { + current.x := (wmax - current.w) / 2 + current.y := (hmax - current.h) / 2 + } + "home": { + current.x := 0 + current.y := 0 + } + "x / 2": current.x <- current.x / 2 + "y / 2": current.y <- current.y / 2 + "x * 2": check(current.x <- current.x * 2) + "y * 2": check(current.y <- current.y * 2) + } | fail + show() + + return + +end + +# Check validity of selection. + +procedure check() + + if + (0 <= current.w <= (wmax - current.x)) & + (0 <= current.h <= (hmax - current.y)) & + (0 <= current.x <= hmax) & + (0 <= current.y <= wmax) + then return else { + Alert() + fail + } + +end + +# Copy hidden source window to a visible window. + +procedure copy_source(label) + + screen := WOpen( + "size=" || WAttrib(source, "width") || "," || WAttrib(source, "height"), + "posx=" || posx, + "posy=" || posy, + "label=" || label, + "drawop=reverse", + "linestyle=onoff" + ) | ExitNotice("Cannot open image window.") + + CopyArea(source, screen) + + Raise(controls) + + wmax := WAttrib(source, "width") + hmax := WAttrib(source, "height") + + WAttrib(pattern, "width=" || (WAttrib(screen, "width"))) + WAttrib(pattern, "height=" || (WAttrib(screen, "height"))) + EraseArea(pattern) + + current := rect(0, 0, wmax, hmax) + + show() + + return + +end + +# File menu callback. + +procedure file_cb(vidget, value) + + case value[1] of { + "open @O": get_image() + "pick @P": pick() + "quit @Q": exit() + "save @S": save_cb() + "save mirrored": mirror_snap() + } + + return + +end + +# Utility procedure to get new source image. + +procedure get_image() + + WClose(\source) + WClose(\screen) + WClose(\symmetry) + EraseArea(pattern) + + repeat { + (OpenDialog("Open image:") == "Okay") | fail + source := WOpen("canvas=hidden", "image=" || dialog_value) | { + Notice("Can't open " || dialog_value || ".") + next + } + copy_source(dialog_value) + wmax := WAttrib(source, "width") + hmax := WAttrib(source, "height") + break + } + + return + +end + +# These values are for Motif; they may need to be changed for other +# window managers. + +$define Offset1 32 +$define Offset2 82 + +# Initialize the program. + +procedure init() + local iheight + + posx := WAttrib(controls, "width") + Offset1 + + iheight := WAttrib(controls, "height") + + pattern := WOpen("label=repeat", "resize=on", "size=" || iheight || + "," || iheight, "posx=" || posx, "posy=10") | + ExitNotice("Cannot open pattern window.") + + posy := WAttrib(pattern, "height") + Offset2 + + sx := vidgets["text"].ax + sy := vidgets["text"].ay + + Raise(controls) + + return + +end + +procedure update() + + # Update selection information on interface. + + WAttrib(controls, "drawop=reverse") + + DrawString(controls, sx, sy, "marquee: x=" || (\previous).x || " y=" || + previous.y || " w=" || previous.w || " h=" || previous.h) + DrawString(controls, sx, sy, "marquee: x=" || current.x || " y=" || + current.y || " w=" || current.w || " h=" || current.h) + + WAttrib(controls, "drawop=copy") + + # Update the selection rectangle. + + DrawRectangle(screen, (\previous).x, previous.y, previous.w, previous.h) + DrawRectangle(screen, current.x, current.y, current.w, current.h) + + previous := copy(current) + + return + +end + +procedure mirror_cb() + + if /source then { + Notice("No source window.") + fail + } + + if current.w < 0 then { + current.w := -current.w + current.x -:= current.w + } + + if current.h < 0 then { + current.h := -current.h + current.y -:= current.h + } + + WClose(\symmetry) + + symmetry := mirror(source, current.x, current.y, current.w, current.h) | { + Notice("Cannot mirror tile.") + fail + } + + # In case the window manager opens a window larger than requested ... + + tile(symmetry, pattern, 0, 0, current.w * 2, current.h * 2) + + # Hide it but keep it in case the user wants to save it. + + WAttrib(symmetry, "canvas=hidden") + + Raise(controls) + + return + +end + +procedure mirror_snap() + + snapshot(\symmetry, 0, 0, current.w * 2, current.h * 2) | { + Notice("No mirrored tile.") + fail + } + + return + +end + +# Utility procedure to let user pick an image file in the current directory. + +procedure pick() + local plist, ls + + plist := filelist("*.gif *.GIF") | { + Notice("Pick not supported on this platform.") + fail + } + + if *plist = 0 then { + Notice("No files found.") + fail + } + + repeat { + if SelectDialog("Select image file:", plist, plist[1]) == "Cancel" + then fail + WClose(\source) + WClose(\screen) + WClose(\symmetry) + EraseArea(pattern) + source := WOpen("canvas=hidden", "image=" || dialog_value) | { + Notice("Cannot open " || dialog_value || ".") + next + } + copy_source(dialog_value) + break + } + + return + +end + +# Callback to terminate program execution. + +procedure quit_cb() + + exit() + +end + +procedure refresh_cb() + + tile(source, pattern, current.x, current.y, current.w, current.h) + + return + +end + +# Callback procedure to allow use of standard tile sizes. + +procedure select_cb(vidget, value) + + if /source then fail + + check(current.w <- current.h <- + case value of { + " 4 x 4": 4 + " 8 x 8": 8 + " 16 x 16": 16 + " 32 x 32": 32 + " 64 x 64": 64 + " 72 x 72": 72 + " 96 x 96": 96 + " 100 x 100": 100 + " 128 x 128": 128 + " 200 x 200": 200 + " 256 x 256": 256 + " 400 x 400": 400 + " 512 x 512": 512 + }) | fail + + show() + + return + +end + +# Callback to allow setting of specific selection rectangle values. + +procedure set_cb() + + repeat { + if TextDialog("Set values:", + ["x", "y", "w", "h"], + [current.x, current.y, current.w, current.h ] + ) == "Cancel" then fail + check( + current.x <- integer(dialog_value[1]) & + current.y <- integer(dialog_value[2]) & + current.w <- integer(dialog_value[3]) & + current.h <- integer(dialog_value[4]) + ) | { + Notice("Invalid value.") + next + } + show() + return + } + +end + +# Keyboard shortcuts. + +procedure shortcuts(e) + + case type(e) of { + "string": { + if &meta then case map(e) of { # fold case + "m": mirror_cb() + "o": get_image() + "p": pick() + "q": exit() + "s": save_cb() + } + } + "integer": { + if &meta then { # nudge dimensions + if check( + case e of { + Key_Left: current.w <- current.w - 1 + Key_Right: current.w <- current.w + 1 + Key_Up: current.h <- current.h - 1 + Key_Down: current.h <- current.h + 1 + } + ) then show() else fail + } + else { # nudge location + if check ( + case e of { + Key_Left: current.x <- current.x - 1 + Key_Right: current.x <- current.x + 1 + Key_Up: current.y <- current.y - 1 + Key_Down: current.y <- current.y + 1 + } + ) then show() else fail + } + } + } + + return + +end + +# Procedure to handle all that goes with a new selection. + +procedure show() + local x, y, w, h + + if /source then { + Notice("No source image.") + fail + } + + x := current.x + y := current.y + w := current.w + h := current.h + + if w < 0 then + x -:= (w := -w) + if h < 0 then + y -:= (h := -h) + + tile(source, pattern, x, y, w, h) + + update() + + return + +end + +# Utility procedure to save current selection. + +procedure save_cb() + + return snapshot(\source, current.x, current.y, current.w, current.h) | { + Notice("No source image.") + fail + } + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui_atts() + return ["size=445,373", "bg=gray-white"] +end + +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:::0,0,445,373:",], + ["dim:Label:::209,34,70,13:dimensions",], + ["dimens:Choice::9:214,55,64,189:",dimens_cb, + ["home","w max","h max","w * 2","h * 2", + "w / 2","h / 2","w = 1","h = 1"]], + ["file:Menu:pull::0,1,36,21:File",file_cb, + ["open @O","pick @P","save @S ","save mirrored","quit @Q"]], + ["line1:Line:::0,22,326,22:",], + ["loc:Label:::120,34,56,13:location",], + ["location:Choice::11:113,55,71,231:",location_cb, + ["nw","ne","se","sw","center", + "x max","y max","x * 2","y * 2","x / 2", + "y / 2"]], + ["mirror:Button:regular::17,126,58,20:mirror",mirror_cb], + ["refresh:Button:regular::17,88,58,20:refresh",refresh_cb], + ["select:Choice::13:309,55,99,273:",select_cb, + [" 4 x 4"," 8 x 8"," 16 x 16"," 32 x 32"," 64 x 64", + " 72 x 72"," 96 x 96"," 100 x 100"," 128 x 128"," 200 x 200", + " 256 x 256"," 400 x 400"," 512 x 512"]], + ["set:Button:regular::17,51,58,20:set",set_cb], + ["size:Label:::341,34,28,13:size",], + ["text:Button:regularno::17,347,10,20:",], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/travels.icn b/ipl/gprogs/travels.icn new file mode 100644 index 0000000..e268405 --- /dev/null +++ b/ipl/gprogs/travels.icn @@ -0,0 +1,1121 @@ +############################################################################ +# +# File: travels.icn +# +# Subject: Program to animate the traveling salesman problem +# +# Author: Gregg M. Townsend +# +# Date: September 17, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: travels [window options] [-q] [npoints] +# +# -q (quiet) suppresses commentary normally written to stdout +# +# npoints seeds the field with that many initial cities +# and sets the count for the "reseed" button +# +# +# travels illustrates several heuristic algorithms for obtaining +# approximate solutions to the traveling salesman problem. Cities may +# be seeded randomly or entered with the mouse. Speed may be controlled +# using a slider. The CPU time, number of cities, and path length are +# displayed on a status line and written to standard output after every +# major action. +# +############################################################################ +# +# Several types of controls are provided. New cities may be added +# at any time, invalidating any current path. At least two cities must +# be seeded before a path can be constructed. A path must be constructed +# before any of the optimization algorithms can be applied. +# +# For a description on of the algorithms used, see: +# David S. Johnson +# Local Optimization and the Traveling Salesman Problem +# Proc. 17th Colloquium on Automata, Languages, & Programming +# Springer-Verlag (1990), pp. 446-461 +# +# +# Mouse Actions: +# +# Clicking the left mouse button adds a new point. +# +# +# Keyboard Actions: +# +# The digit 0 clears all points. +# The digits 1 through 9 seed 1 to 81 (n ^ 2) new points. +# +# Each of the pushbuttons below also has a keyboard equivalent +# which is indicated on the pushbutton. +# +# +# Pushbuttons: +# +# Removing and adding points: +# Clear Remove all points +# Reseed Add n random points (a command option, default 20) +# +# Path construction: +# Initial Connect points in order of initialization +# Random Random path +# Strip Strip-wise construction +# NearNbr Nearest-neighbor algorithm +# NearIns Nearest-insertion algorithm +# FarIns Farthest-insertion algorithm +# Greedy Greedy algorithm +# +# Optimizations: +# 2-Adj Swap pairs of adjacent points +# Uncross Swap pairs of intersecting segments +# 2-Opt Swap all segment pairs that shorten the path +# +# Control: +# List List coordinates of points on standard output +# Refresh Redraw the screen +# Quit Exit the program +# +# +# Delay Slider: +# +# The delay slider can be used to slow down the action. It specifies a +# number of milliseconds to pause before visiting a new point or drawing +# a new path segment. Its response is nonlinear in order to allow finer +# control of short delays. Delays are inexact due to system granularity +# and other problems. +# +# Unfortunately, the delay slider can only be changed between actions, +# not during construction or optimization. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, button, slider, evmux, random, graphics +# +############################################################################ + +link options +link optwindw +link button +link slider +link evmux +link random +link graphics + +$define EColor "dark blue" # emphasis color + + + +global ptlist # list of point records (permanent id order, not route) +record point( + id, # permanent id + x, y, # location + nxt, prv, # forward and backward links for route + t1, t2) # scratch cells for traversal algorithms + + + +global distlist # list of distance recs (linearized triangular matrix) +global distsrt # sorted distance list (created when needed) +record dstrec( + d, # distance between two points (x1000, stored as int) + p, q) # the two points + + + +global newpts # non-null if points are new since last report + +global havepath # non-null if we have a valid path + # (start from any point and follow links) + +global lastclk # value of &time before last computation +global delaytime # delay time between steps, in msec + +global opts # command line options +global nseed # number of points to seed + +global win # main window +global fgwin # binding for drawing in foreground color +global emwin # binding for drawing in emphasis color +global bgwin # binding for erasing in background color + +global m, w, h, bw, bh, fh # screen layout parameters +global ax, ay, aw, ah # corners and size of arena + + + + +######################### main program ######################### + + + +procedure main(args) + local base, pt, sg, hl + + # get options and open a window + opts := options(args, "qE:" || winoptions()) # get options + + /opts["W"] := 700 # default width + /opts["H"] := 500 # default height + /opts["E"] := EColor # default emphasis + /opts["T"] := "sans,bold,12" # default font + /opts["M"] := -1 # use standard margin + win := optwindow(opts, "linewidth=2") # open window + m := opts["M"] # save specified margin + h := opts["H"] # save usable height + w := opts["W"] # save usable width + + bw := 100 # button width + bh := 18 # button height + fh := 20 # footer height + + ax := m + bw + m # arena bounds and size + ay := m + aw := w - bw - m + ah := h - fh - m + + fgwin := Clone(win) + emwin := Clone(win, "fg=" || (opts["E"] | EColor | "black"), "linewidth=4") + bgwin := Clone(win, "fg=" || Bg(win), "linewidth=4") + + # set up sensor for adding points + sensor(win, &lrelease, addpt, &null, ax, ay, aw, ah) + + # set up buttons + buttonrow(win, m, m, bw, bh, 0, bh + (2 > m | 2), + "seeding", &null, &null, + "Clear 0", argless, clrpts, + "Reseed D", argless, reseed, + &null, &null, &null, # spacing + "construction", &null, &null, + "Initial I", argless, initpath, + "Random R", argless, randpath, + "Strip S", argless, strippath, + "NearNbr B", argless, nearnbr, + "NearIns N", argless, nearins, + "FarIns F", argless, farins, + "Greedy G", argless, greedypath, + &null, &null, &null, + "optimization", &null, &null, + "2-Adj A", argless, twoadj, + "Uncross U", argless, uncross, + "2-Opt T", argless, twoopt, + &null, &null, &null, + "control", &null, &null, + "Refresh H", argless, refresh, + "List L", argless, listpath, + &null, &null, &null, + "Quit Q", argless, exit, + ) + + # set up corresponding keyboard handlers + quitsensor(win) # q and Q + sensor(win, 'Ii', argless, initpath) + sensor(win, 'Rr', argless, randpath) + sensor(win, 'Ss', argless, strippath) + sensor(win, 'Bb', argless, nearnbr) + sensor(win, 'Nn', argless, nearins) + sensor(win, 'Ff', argless, farins) + sensor(win, 'Gg', argless, greedypath) + sensor(win, 'Aa', argless, twoadj) + sensor(win, 'Uu', argless, uncross) + sensor(win, 'Tt', argless, twoopt) + sensor(win, 'Ll', argless, listpath) + sensor(win, 'Dd', argless, reseed) + sensor(win, 'Hh', argless, refresh) + sensor(win, '0', argless, clrpts) + sensor(win, '123456789', reseed) + + # set up speed slider + slider(win, setdly, 0, m, m + h - bh, bw, bh, 0, 0, 1) + setdly(win, 0, 0) + + # initialize + randomize() + clrpts() + lastclk := &time + + if nseed := integer(args[1]) then + reseed() + else + nseed := 20 + + # process events + evmux(win) +end + + + +# setdly(win, arg, value) -- set delay time + +procedure setdly(win, arg, value) + local s, l + + value := integer(10001 ^ value + 0.5) - 1 + delaytime := value + s := " delay " || value || " " + l := TextWidth(win, s) + GotoXY(win, m + (bw - l) / 2, m + h - bh - m / 2) + writes(win, s) + return +end + + + +# pause() -- delay according to the current setting + +procedure pause() + if delaytime > 0 then + WDelay(win, delaytime) + return +end + + + +######################### path constructions ######################### + + + +# initpath() -- connect in initial placement order + +procedure initpath() + local i + + bgnpath(0, "placement order...") | fail + ptlist[1].nxt := &null + every i := 2 to *ptlist do { + follow(ptlist[i-1], ptlist[i]) + pause() + } + ptlist[-1].nxt := ptlist[1] + ptlist[1].prv := ptlist[-1] + drawpath(fgwin, ptlist[-1], ptlist[1]) + + havepath := 1 + report("initial path") + return +end + + + +# randpath() -- make random connections + +procedure randpath() + local l, i, p, q + + bgnpath(0, "connecting randomly...") | fail + + l := copy(ptlist) # get copy of point list + every i := 1 to *l do # shuffle it + l[i] :=: l[?i] + + p := l[1] + q := l[-1] + p.nxt := &null + every i := 2 to *l do { + follow(l[i-1], l[i]) + pause() + } + p.prv := q + q.nxt := p + drawpath(fgwin, q, p) + + havepath := 1 + report("random path") + return +end + + + +# strippath() -- construct using strips + +procedure strippath() + local i, l, n, p, q, r + + if *ptlist < 3 then + return + bgnpath(0, "stripwise algorithm") + + n := integer(sqrt(*ptlist) + .5) + l := list(n) + every !l := list() + + every p := !ptlist do { + i := integer(1 + n * (p.x - ax) / real(aw + 1)) + put(l[i], p) + } + + every i := 1 to n do + l[i] := sortf(l[i], 3) + every i := 2 to n by 2 do { + r := [] + every push(r, !l[i]) + l[i] := r + } + + q := !!l # get first point from first non-empty bin + every p := !!l do { + q.nxt := p + p.prv := q + drawpath(fgwin, q, p) + q := p + pause() + } + q := !!l + p.nxt := q + q.prv := p + drawpath(fgwin, p, q) + + havepath := 1 + report("stripwise algorithm") + return +end + + + +# nearnbr() -- nearest neighbor + +procedure nearnbr() + local f, p, q, s, d + + bgnpath(1, "nearest neighbor...") | fail + + f := p := ?ptlist + p.nxt := p.prv := &null + s := set([p]) + while *s < *ptlist do { + every d := !distsrt do { + if d.p === p then + q := d.q + else if d.q === p then + q := d.p + else + next + if member(s, q) then + next + insert(s, q) + p := follow(p, q) + p.nxt := &null + pause() + break + } + } + p.nxt := f + f.prv := p + drawpath(fgwin, p, f) + + havepath := 1 + report("nearest neighbor") + return +end + + + +# nearins() -- make path using nearest-insertion algorithm + +procedure nearins() + local d, p, q, t, todo, mind + + bgnpath(0, "nearest insertion...") | fail + + # init path with the two closest points + mind := 1000000000 + every d := !distlist do + if mind >:= d.d then { + p := d.p + q := d.q + } + p.nxt := p.prv := q + q.nxt := q.prv := p + drawpath(fgwin, p, q) + pause() + + todo := set(ptlist) # set of points not yet on path + every delete(todo, p | q) + + every t := !todo do + t.t1 := dist(t, q) # point.t1 = distance to nearest point on path + + while *todo > 0 do { # repeat for each new point added to path + mind := 1000000000 # mind = minimum distance this pass + every t := !todo do { + t.t1 >:= dist(t, p) # update pt's dist to path if latest pt closer + if mind >:= t.t1 then # check for better (smaller) min d this pass + q := t # if nearest so far + } + # point q is the remaining point nearest from any point on the path + joinpath(p, q) + delete(todo, q) + pause() + p := q + } + + havepath := 1 + redraw() + report("nearest insertion") + return +end + + + +# farins() -- make path using farthest-insertion algorithm + +procedure farins() + local d, p, q, t, todo, maxd + + bgnpath(0, "farthest insertion...") | fail + + # init path with the two most distant points + maxd := -1 + every d := !distlist do + if maxd <:= d.d then { + p := d.p + q := d.q + } + p.nxt := p.prv := q + q.nxt := q.prv := p + drawpath(fgwin, p, q) + pause() + + todo := set(ptlist) # set of points not yet on path + every delete(todo, p | q) + + every t := !todo do + t.t1 := dist(t, q) # point.t1 = distance to nearest point on path + + while *todo > 0 do { # repeat for each new point added to path + maxd := -1 # maxd = furthest distance this pass + every t := !todo do { + t.t1 >:= dist(t, p) # update pt's dist to path if latest pt closer + if maxd <:= t.t1 then # check for better (larger) maxd this pass + q := t # if farthest so far + } + # point q is the remaining point farthest from any point on the path + joinpath(p, q) + delete(todo, q) + pause() + p := q + } + + havepath := 1 + redraw() + report("farthest insertion") + return +end + + + +# joinpath(p, q) -- add q at best place in path beginning at p + +procedure joinpath(p, q) + local start, best, d + + d := dist(p, q) + dist(q, p.nxt) - dist(p, p.nxt) + start := best := p + while (p := p.nxt) ~=== start do + if d >:= dist(p, q) + dist(q, p.nxt) - dist(p, p.nxt) then + best := p + + follow(best, q) + return +end + + + +# greedypath() -- make path using greedy algorithm + +procedure greedypath() + local p, q, d, g, need + + bgnpath(1, "greedy algorithm...") | fail + + every p := !ptlist do { + p.nxt := p.prv := &null + p.t1 := p.id # point.t1 = group membership + p.t2 := 0 # point.t2 = degree of node + } + + need := *ptlist # number of edges we still need + + every d := |!distsrt do { # |! is to handle 2-pt case + p := d.p + q := d.q + if p.t2 > 1 | q.t2 > 1 then # if either is fully connected + next + if p.t1 = q.t1 & need > 1 then # if would be cycle & not done + next + + # now we are committed to adding the point + pause() + DrawLine(fgwin, p.x, p.y, q.x, q.y) # draw new edge + p.t2 +:= 1 # increase degree counts + q.t2 +:= 1 + + if /p.nxt <- q & /q.prv := p then { # if q can follow p easily + g := q.t1 ~=:= p.t1 | break # break if the final connection + while q := \q.nxt do + q.t1 := g + } + else if /q.nxt <- p & /p.prv := q then { # if p can follow q easily + g := p.t1 ~=:= q.t1 | break # break if the final connection + while p := \p.nxt do + p.t1 := g + } + else if /p.nxt := q then { # implies /q.nxt -- both are chain tails + g := p.t1 + repeat { + q.t1 := g + q.nxt := q.prv + q.prv := p + p := q + q := \q.nxt | break + } + } + else { # /p.prv & /q.prv -- both are chain heads + p.prv := q + g := p.t1 + repeat { + q.t1 := g + q.prv := q.nxt + q.nxt := p + p := q + q := \q.prv | break + } + } + + if (need -:= 1) = 0 then # quit when have all edges + break + } + + havepath := 1 + report("greedy algorithm") + return +end + + + + +# bgnpath(i, msg) -- common setup for path construction +# +# i > 0 if *sorted* distance table will be needed +# msg is status message + +procedure bgnpath(i, msg) + if *ptlist < 2 then + fail + prepdist(i) + status(msg) + if \havepath then + erasepath() + havepath := &null + lastclk := &time + return +end + + + +######################### optimizations ######################### + + + +# twoadj() -- swap pairs of adjacent points + +procedure twoadj() + local lastchg, nflips, p, q + + if /havepath then + return + status("2-adj...") + lastclk := &time + nflips := 0 + + lastchg := p := ?ptlist # pick random starting point + + repeat { + + q := p.nxt.nxt + repeat { + DrawLine(emwin, p.x, p.y, p.nxt.x, p.nxt.y) # mark current spot + if not pairtest(p, q) then # if swap doesn't help + break + flip(p, q) # do the swap + nflips +:= 1 # count it + lastchg := p # update point of last change + } + + pause() + p := p.nxt + if p === lastchg then + break # have made complete circuit without changes + } + + report("2-adj (" || nflips || " flips)") + refresh() + return +end + +procedure adjtest(p, q) + return ((p.nxt.nxt === q) | (q.nxt.nxt === p)) & pairtest(p, q) +end + + + +# twoopt() -- swap segments if total path shortens + +procedure twoopt() + pairdriver("2-opt", pairtest) + return +end + +# pairtest(p, q) -- succeed if swapping out-segments from p and q shortens path + +procedure pairtest(p, q) + return (dist(p,q) + dist(p.nxt,q.nxt)) < (dist(p,p.nxt) + dist(q,q.nxt)) & + (not (p === (q.prv | q | q.nxt))) +end + + + +# uncross() -- swap intersecting segments + +procedure uncross() + pairdriver("uncross", intersect) + return +end + +# intersect(p, q) -- succeed if outward segments from p and q intersect +# +# from comp.graphics.algorithms FAQ, by O'Rourke + +procedure intersect(p, q) + local a, b, c, d + local xac, xdc, xba, yac, ydc, yba + local n1, n2, d12, r, s + + a := p + b := p.nxt + c := q + d := q.nxt + xac := a.x - c.x + xdc := d.x - c.x + xba := b.x - a.x + yac := a.y - c.y + ydc := d.y - c.y + yba := b.y - a.y + + n1 := yac * xdc - xac * ydc + n2 := yac * xba - xac * yba + d12 := real(xba * ydc - yba * xdc) + + if d12 = 0.0 then + fail # lines are parallel or coincident + + r := n1 / d12 + s := n2 / d12 + + # intersection point is: (a.x + r * xba, a.y + r * yba) + + if 0.0 < r < 1.0 & 0.0 < s < 1.0 then + return # segments AB and CD do intersect + else + fail # segments do not intersect (though extensions do) +end + + + + +# pairdriver(label, tproc) -- driver for "uncross" and "2-opt" + +procedure pairdriver(label, tproc) + local slist, todo, nflips, a, p, q + + if /havepath then + return + status(label || "...") + lastclk := &time + nflips := 0 + + slist := list() # initial list of segments + every put(slist, path()) + todo := set() # segments to reconsider + + while p := get(slist) | ?todo do { # pick candidate segment + + delete(todo, p) + pause() + + # restart search every time p's outgoing edge changes + repeat { + + DrawLine(emwin, p.x, p.y, p.nxt.x, p.nxt.y) # mark segment in progress + + # check for swap with every other edge + every q := !ptlist do { + + if tproc(p, q) then { # if test procedure succeeds, + # a swap is worthwhile + + # the path from p.nxt through q will reverse direction; + # this will change segment labelings; so fix up "todo" set + a := q.prv + while a ~=== p do { + if member(todo, a) then { # if segment is on list + delete(todo, a) # remove under old name + insert(todo, a.nxt) # add under new name + } + a := a.prv + } + + # new segment from p will be done when we loop again + # other new segment to list + insert(todo, p.nxt) # add to list + + # now flip the edges + flip(p, q) # flip the edges + nflips +:= 1 # count the flip + + break next # restart search loop using new edge + } + } + + break # if no improvement for one full loop + } + + } + + report(label || " (" || nflips || " flips)") + refresh() + return +end + + + +######################### point maintenance ######################### + + + +# clrpts() -- remove all points + +procedure clrpts() + ptlist := [] + distlist := [] + distsrt := [] + havepath := &null + refresh() + fillrect(bgwin) + status("0 points") + return +end + + + +# reseed() -- add random points to the list + +procedure reseed(win, dummy, x, y, event) + local p, v, n + + n := integer(\event)^2 | nseed + every 1 to n do + addpt(win, &null, ax + ?aw, ay + ?ah) + return +end + + + +# addpt(win, dummy, x, y) -- add one point to the list + +procedure addpt(win, dummy, x, y) + local n, p, q + + if \havepath then { + erasepath() + havepath := &null + } + n := *ptlist + p := point(n + 1, x, y) + every q := !ptlist do + put(distlist, dstrec(integer(1000 * sqrt((q.x-x)^2 + (q.y-y)^2)), p, q)) + put(ptlist, p) + drawpt(p) + status(*ptlist || " points") + newpts := 1 + return p +end + + + +# prepdist(i) -- prepare distance data for path construction +# +# copy the distance list, if not already done, so it can be indexed quickly. +# also create the sorted list if i > 0. + +procedure prepdist(i) + static c, n + + if c ~=== distlist | n ~= *distlist then { + c := distlist := copy(distlist) + n := *distlist + } + if \i > 0 & *distsrt < *distlist then { + status("sorting distances... ") + lastclk := &time + WFlush(win) + distsrt := sortf(distlist, 1) + report("distance sort") + } + return +end + + + +# dist(p, q) -- return distance between p and q assuming p ~=== q + +procedure dist(p, q) + local m, n + m := p.id + n := q.id + if m < n then + m :=: n + return distlist[((m - 1) * (m - 2)) / 2 + n].d +end + + + +# path() -- generate current path, even if it changes during generation + +procedure path() + local l, p, q + p := q := ptlist[1] | fail + l := [p] + while (p := p.nxt) ~=== q do + put(l, p) + suspend !l +end + + +# follow(p, q) -- insert q to follow p (erases old path from p, draws new) + +procedure follow(p, q) + DrawLine(bgwin, p.x, p.y, (p.prv~===\p.nxt).x, p.nxt.y) + every drawpt(p | \p.nxt) + q.nxt := p.nxt + q.prv := p + (\p.nxt).prv := q + p.nxt := q + DrawLine(fgwin, p.x, p.y, q.x, q.y) + DrawLine(fgwin, q.x, q.y, (\q.nxt).x, q.nxt.y) + return q +end + + + +# flip(p, q) -- link p to q, and their successors to each other + +procedure flip(p, q) + local a, b + + DrawLine(bgwin, p.x, p.y, p.nxt.x, p.nxt.y) + DrawLine(bgwin, q.x, q.y, q.nxt.x, q.nxt.y) + # relink half of the chain backwards + a := q + while a ~=== p do { + a.prv :=: a.nxt + a := a.nxt + } + a := p.nxt + b := q.prv + p.nxt := q + q.prv := p + a.nxt := b + b.prv := a + DrawLine(fgwin, p.x, p.y, q.x, q.y) + DrawLine(fgwin, a.x, a.y, b.x, b.y) + every drawpt(p | q | a | b) + return +end + + + +# linkpath(p, q, ...) -- link points p, q, ... in order + +procedure linkpath(l[]) + local i, p, q, v + i := p := get(l) + v := [fgwin, p.x, p.y] + every q := !l do { + p.nxt := q + q.prv := p + p := q + put(v, p.x, p.y) + } + DrawLine ! v + every drawpt(i | !l) + return +end + + + + +######################### drawing ######################### + + + +# refresh() -- redraw screen to repair segments and points + +procedure refresh() + fillrect(bgwin) # erase segs + redraw() + return +end + + + +# redraw() -- redraw path without erasing + +procedure redraw() + local p + + every drawpt(!ptlist) + every p := !ptlist do + DrawLine(fgwin, p.x, p.y, (\p.nxt).x, p.nxt.y) + return +end + + + +# erasepath() -- erase path, redraw points if necessary + +procedure erasepath() + local l, p, v + + v := [bgwin] + every p := ptlist[1].prv | path() do + put(v, p.x, p.y) + DrawLine ! v + every drawpt(!ptlist) + return +end + + + +# drawpath(win, p, q) -- draw the path from p to q +# +# (of course, depending on the foreground color, this can hide a path, too.) + +procedure drawpath(win, p, q) + local v + + v := [win, p.x, p.y] + while p ~=== q do { + p := p.nxt + put(v, p.x) + put(v, p.y) + } + DrawLine ! v + return +end + + + +# drawpt(p) -- draw the single point p + +procedure drawpt(p) + FillRectangle(fgwin, p.x - 2, p.y - 2, 5, 5) + return +end + + + +# fillrect(win) -- fill the working area + +procedure fillrect(win) + FillRectangle(win, ax - m + 1, ay - m + 1, aw + 2 * m - 1, ah + 2 * m - 1) + return +end + + + +######################### reporting ######################### + + + +# listpath() -- list the coordinates of each point on standard output + +procedure listpath() + local p + + if \havepath then { + write("\point list in order of traversal:") + every listpt(path()) + } + else { + write("\point list (no path established):") + every listpt(!ptlist) + } + return +end + +# listpt(p) - list one point + +procedure listpt(p) + write(right(p.id, 3), ".", right(p.x, 5), right(p.y, 5), + right((\p.prv).id | "", 6), right((\p.nxt).id | "", 6)) + return +end + + + +# report(text) -- display statistics on screen and stdout +# +# The statistics include the delta time since lastclk was last set. +# +# Output to stdout is suppressed if the "-q" option was given. +# Output to stdout is double spaced if the set of points has changed. + +procedure report(text) + local p, n, d, s, dt + + dt := ((((&time - lastclk) / 1000.0) || "000") ? (tab(upto(".")) || move(3))) + s := right(*ptlist, 4) || " pts " + + if \havepath then { + d := 0 + every p := !ptlist do + d +:= dist(p, p.nxt) + d := (d + 500) / 1000 + s ||:= right("d = " || d, 10) + } + else + s ||:= " " + + s ||:= right(dt , 8) || " sec " || text + + status(s) + if /opts["q"] then { + if \newpts then + write() + write(s) + } + newpts := &null + return +end + + +# status(s) -- write s as a status message + +procedure status(s) + EraseArea(win, m + bw + m, m + h - fh) + GotoXY(win, m + bw + m, m + h - (fh / 4)) + writes(win, s) + return +end diff --git a/ipl/gprogs/trkvu.icn b/ipl/gprogs/trkvu.icn new file mode 100644 index 0000000..9cd3c36 --- /dev/null +++ b/ipl/gprogs/trkvu.icn @@ -0,0 +1,695 @@ +############################################################################ +# +# File: trkvu.icn +# +# Subject: Program to display GPS track logs +# +# Authors: Gregg M. Townsend +# +# Date: October 1, 2005 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Trkvu displays GPS track logs, using color to indicate various +# characteristics such as velocity, direction, or time of day. +# +############################################################################ +# +# usage: trkvu file... +# +# Each file argument is a track log uploaded from a GPS receiver. +# Lines that end in three decimal values specify latitude, longutude, +# and altitude in that order. Lines with just two values omit the +# altitude. Lines without data indicate breaks between segments. +# +# Some colorings use timestamps from the track logs. A timestamp +# has the form "mm/dd/yyyy hh:mm:ss" or "yyyy/mm/dd hh:mm:ss" and +# precedes the latitude and longitude. +# +############################################################################ +# +# Track log colorings are selected by pressing a key: +# +# F color by File +# A color by Age +# O color by Orientation (direction of travel) +# V color by Velocity +# I color by Interval duration (GPS sample rate) +# S color Segments in contrasting colors +# Y color by time of Year +# D color by Day of week +# H color by Hour of day +# M color by Minute (repeating colors every 10 minutes) +# T color by Time of day +# +# Colorings can also be cycled: +# +# SP or CR cycle to next coloring +# BS or DEL cycle to preceding coloring +# +# A legend explains each coloring. If it shows individually labeled +# color blocks, the colors encode discrete values. If a spectrum +# is shown, the colors vary smoothly over a continuous range. +# +# Some colorings require timestamps. For these, tracks lacking +# timestamps are drawn in gray. +# +############################################################################ +# +# Zooming and Panning: +# +# To zoom to a particular region, sweep out the region using the +# left mouse button. To cancel a sweep, reduce its width or height +# to fewer than ten pixels. +# +# The window may be resized as desired. +# +# The following keyboard commands also affect the display region: +# +# + or = zoom in +# - or _ zoom out +# 0 or Home zoom to initial view +# arrow keys pan the display (hold Shift key for smaller pan) +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: datetime, graphics, mapnav, strings +# +############################################################################ + + +$include "keysyms.icn" + +link datetime +link graphics +link mapnav +link strings + +$define BORDER 10 # border widths + + +record view( # one view of data + cs, # cset of chars to select this view + ltitle, # legend title + hproc, # hue selection procedure + lproc) # legend procedure + +record point( # one point along a track + t, # time at point (real days & fraction since epoch) + x, y, # coordinates of point (longitude, latitude) + fhue) # hue assigned to original source file + + +global viewlist # list of views (view records) +global curview # current selected view + +global huelist # list of ColorValues of 180 hues + +global fnlist # file name list (for F legend) +global fhlist # file hue list (for F legend) + +global seglist # list of travel segments +global tmin, tmax # earliest and latest time seen +global xmin, xmax # westernmost and easternmost longitude seen +global ymin, ymax # northernmost and southernmost latitude seen + +global lbase # legend baseline y value +global lclip # clipping arguments for legend region +global mclip # clipping arguments for map region +global stdwin # std bg/fg window + + + +# ========================= Overall Control ========================= + +procedure main(args) + local e, v, xywh + + Window("size=800,800", "resize=on", "canvas=hidden", + "linewidth=2", "font=sans,bold,12", args) + stdwin := Clone("bg=white") + + viewlist := [ + # sequence here is followed by <SP> and <BS> + view('Ff', "File", byfile, flegend), + view('Aa', "Age", byage, agelegend), + view('Oo', "Orientation", orientation, olegend), + view('Vv', "Velocity", velocity, vlegend), + view('Ii', "Interval", byinterval, intlegend), + view('Ss', "Segments", segments, seglegend), + view('Yy', "time of Year", bymonth, monthlegend), + view('Dd', "Day", byday, daylegend), + view('Hh', "Hour", byhour, hourlegend), + view('Mm', "Minute", byminute, minutelegend), + view('Tt', "Time", bytime, timelegend), + ] + while /viewlist[-1] do pull(viewlist) + + seglist := [] # init data structures + fnlist := [] + fhlist := [] + + every load(!args) # load data + survey() # find extremes + fnlist := fnsimp(fnlist) # simplify filename list + + WAttrib("canvas=normal") # make display visible + hueinit() # init color manager + layout() # lay out display + mapinit(draw, , xmin, xmax, ymax, ymin, cos(dtor((ymin + ymax) / 2))) + + if *args > 1 then + Enqueue("f") # show initially by file + else if tmax > 0 then + Enqueue("a") # show initially by age + else + Enqueue("o") # show initially by orientation + + # ==================== main event loop ==================== + + while e := Event() do { + if upto((v := \!viewlist).cs, e) then { # if a view selector + curview := v + EraseArea() + mapgen() # regenerate map + } + else case e of { + !" \n\r": nextview(+1) # cycle view forward + !"\b\d": nextview(-1) # cycle view backward + &resize: { layout(); mapevent(e) } # resize window + default: { mapevent(e) } # possible standard action + } + } +end + +procedure nextview(d) # advance to next view in sequence + local i + + every i := 1 to *viewlist do + if curview === viewlist[i] then { + i := (i + *viewlist - 1 + d) % *viewlist + 1 + curview := viewlist[i] + mapgen() + return + } +end + + + +# ========================= Input ========================= + +procedure load(fname) # load data from one file + local f, h, p, w, t, x, y, a, line, ptlist + static n + initial n := 0 + + f := open(fname) | stop("cannot open ", fname) + h := huenum(n +:= 1) + put(fnlist, fname) + put(fhlist, h) + while line := read(f) do { + every put(w := [], words(line)) + if -90.0 <= numeric(w[-3]) <= 90.0 then + a := pull(w) # altitude + if x := numeric(w[-1]) & y := numeric(w[-2]) then { + t := tcrack(w[-4], w[-3]) | &null + /ptlist := [] + put(ptlist, p := point(t, x, y, h)) + } + else { + put(seglist, \ptlist) + ptlist := &null + next + } + } + + put(seglist, \ptlist) + close(f) + if /p then + write(&errout, " no data: ", fname) + return +end + +procedure tcrack(date, time) # translate date + time into real value + local day, sec + static smul + initial smul := 1.0 / (24 * 60 * 60) + + if date[3] == "/" then + date := map("CcYy/Mm/Dd", "Mm/Dd/CcYy", date) + if date == ("1989/12/31" | "1990/01/01") then + return &null + *time = 8 | fail + *date = 10 | fail + day := DateToSec(date) | fail + sec := ClockToSec(time) | fail + return smul * (day + sec) +end + +procedure survey() # survey data ranges + local p + + xmin := 180 + xmax := -180 + ymin := 90 + ymax := -90 + tmin := 100 * 365.25 + tmax := 0 + + every p := !!seglist do { + tmin >:= \p.t + tmax <:= \p.t + xmin >:= p.x + xmax <:= p.x + ymin >:= p.y + ymax <:= p.y + } + + if xmin > xmax then + stop(" nothing to display") # diagnostic already issued + + if tmin > tmax then + tmin := tmax := 0 + + return +end + +procedure fnsimp(fnlist) # simplify filename list + local f, i, j, s + + if *fnlist < 2 then fail + (coprefix ! fnlist) ? { + i := 1 + while i := upto('/') + 1 do + move(1) + } + (cosuffix ! fnlist) ? { + tab(upto('.') | 0) + j := -*tab(0) + } + f := [] + every put(f, (!fnlist)[i:j]) + return f +end + + + +# ========================= Color Management ========================= +# +# Map colors are taken from the fully saturated color spectrum, spaced +# every 2 degrees in HSV space. This yields 180 different colors, well +# within Icon's limit of 256. The greens are darkened a bit for better +# contrast with the white background; but the yellows are not, because +# a darkened yellow is really ugly. (For better contrast, some colorings +# use hue 55 instead of 60 for a yellow color.) + +procedure hueinit() # initialize hue table (360 entries) + local d, d2, v + + huelist := list(360) + every d := 0 to 359 do { + d2 := d - d % 2 # use 2-degree quanta + if 60 < d2 < 180 then # darken green region + v := integer(100 - 0.8 * (60 - abs(d2 - 120))) + else + v := 100 + huelist[d + 1] := HSVValue(d2 || "/100/" || v) + } + return +end + +procedure sethue(h) # set & cache color, given hue in degrees >= 0 + local k + static kprev + + if h := integer(h) % 360 then + k := huelist[h + 1] + else # use gray for invalid argument + k := "gray" + Fg(kprev ~===:= k) + return +end + +procedure huenum(n) # return hue from ordered list + static predef + initial predef := [240, 0, 120, 30, 180, 300, 50, 270, 70, 210, 330] + # blu red grn org cyan mgnta tan purp grn blu plum + + return predef[n] | (137 * n) % 360 +end + + + +# ========================= Map Drawing ========================= + +procedure layout() # configure window layout + local w, h, lh + + Bg("pale weak yellow") + Clip() + EraseArea() + Bg("white") + + w := WAttrib("width") + h := WAttrib("height") + + # set legend size and baseline + lh := 2 * BORDER + WAttrib("ascent") + lbase := BORDER + lh - BORDER + + # set legend clipping, and clear + lclip := [BORDER, BORDER, w - 2 * BORDER, lh] + Clip ! ([stdwin] ||| lclip) + Clip ! lclip + EraseArea() + + # set map clipping, and clear + mclip := [BORDER, lh + 2 * BORDER, w - 2 * BORDER, h - lh - 3 * BORDER] + Clip ! mclip + EraseArea() + + return +end + +procedure draw(win, pjn, a) # display map using curview + local ptlist, h, n, p, q, x1, y1, x2, y2, l + + Clip ! lclip + EraseArea() + GotoXY(2 * BORDER, lbase) + ltext(curview.ltitle) + ltext(": ") + curview.lproc() + + Clip ! mclip + every ptlist := !seglist do { + if *Pending() > 0 then break + p := &null + every q := !ptlist do { + l := project(pjn, [q.x, q.y]) + x2 := integer(get(l)) + y2 := integer(get(l)) + x2 <:= -32767 + y2 <:= -32767 + x2 >:= 32767 + y2 >:= 32767 + if \p then { + sethue(curview.hproc(p, q) | &null) + DrawLine(x1, y1, x2, y2) + } + else if *ptlist = 1 then { + sethue(curview.hproc(q, q) | &null) + FillRectangle(x2 - 1, y2 - 1, 3, 3) + } + p := q + x1 := x2 + y1 := y2 + } + } + return +end + + + +# ========================= Legend Writing ========================= +# +# Colors are written via &window, text in black via stdwin. + +procedure ltext(s) # write text + + return WWrites(stdwin, s) +end + +procedure lhue(h, t) # write hue block with optional caption + local x, w + + sethue(h) + x := WAttrib("x") + w := WAttrib("ascent") + FillRectangle(x, lbase + 1, w - 1, -w) + GotoXY(x + w, lbase) + ltext(\t) + return +end + +procedure lspectrum(h1, h2, n) # write spectrum of 6 colors from h1 to h2 + local i, m + + /n := 6 + m := (h2 - h1) / (n - 1.0) + every i := 1 to n do + lhue(h1 + m * (i - 1)) + return +end + + + +# ========================= View Procedures ========================= +# +# View procedures are paired: a legend procedure draws the legend and a +# hue selection procedure that chooses the hue for each segment. (Hue +# procedure return a value in degrees, or they fail, which draws gray.) + + +# F: color segments by source file, using colors set at load time + +procedure flegend() + local i + + every i := 1 to *fnlist do + lhue(fhlist[i], fnlist[i] || " ") + return +end + +procedure byfile(p, q) + return q.fhue +end + + +# A: color segments by age (relative to range of timestamps seen) + +procedure agelegend() + + ltext("oldest") + lspectrum(630, 360, 12) + ltext("newest") + return +end + +procedure byage(p, q) + + # purple oldest, green mid, red newest + return 630. - 270. * (\q.t - tmin) / (tmax - tmin) +end + + +# O: color segments by orientation (direction of travel) + +procedure olegend() + + ltext("N"); lspectrum(270, 180) + ltext("E"); lspectrum(180, 90) + ltext("S"); lspectrum(90, 0) + ltext("W"); lspectrum(360, 270) + ltext("N") + return +end + +procedure orientation(p, q) + + # blue north, teal east, olive south, red west + return 180. + rtod(atan(q.y - p.y, cos(dtor(q.y)) * (q.x - p.x))) +end + + +# V: color segments by velocity + +procedure vlegend() + + lhue(240, "1 ") + lhue(210, "2 ") + lhue(180, "3 ") + lhue(120, "4 ") + lhue( 55, "5 ") + lhue( 30, "6 ") + lhue( 0, "7 ") + lhue(300, "8 ") + lhue(270, "9 ") + ltext(" mph (x1, x10, ...)") + return +end + +procedure velocity(p, q) + local dt, dx, dy, d, mph + static hues + initial hues := [270, 240, 210, 180, 120, 55, 30, 0, 300, 270] + # 0 1 2 3 4 5 6 7 8 9 + # 10 20 30 40 50 60 70 80 90 + # 100 200 300 400 500 600 700 800 900 + + dt := 0 < (\q.t - \p.t) | fail + dx := cos(dtor(p.y)) * (q.x - p.x) + dy := q.y - p.y + d := sqrt(dx ^ 2 + dy ^ 2) + mph := integer(2.877 * d / dt + 0.5) + while mph > 9 do + mph /:= 10 + return hues[mph + 1] +end + + +# I: color segments by length of time interval + +procedure intlegend() + + lhue( 0, "0 ") + lhue( 30, "1 ") + lhue( 55, "2 ") + lhue(120, "4 ") + lhue(180, "8 ") + lhue(220, "16 ") + lhue(240, "32 ") + lhue(290, "64 sec") + return +end + +procedure byinterval(p, q) + local dt, i + static hues + initial hues := [0, 30, 55, 120, 180, 220, 240, 290] + # 0 1 2 4 8 16 32 64 + + dt := integer(86400. * (\q.t - \p.t) + 0.5) | fail + i := (2 + integer(log(0 < dt, 2))) | 1 + return hues[i | -1] +end + + +# S: emphasize individual segments in contrasting colors. + +procedure seglegend() + + lspectrum(137, 12*137, 12) + ltext("...") + return +end + +procedure segments(p, q) + static n + initial n := 0 + + return n +:= 137 +end + + +# Y: color segments by time of year as a spectrum + +procedure monthlegend() + + ltext("January") + lspectrum(525, 195, 12) + ltext("December") + return +end + +procedure bymonth(p, q) + + # cyan winter, green spring, red summer, blue fall + return 540. - (\q.t % 365.25) * (360. / 365.25) +end + + +# D: color segments by day of week + +procedure daylegend() + + lhue(240, "Sun ") + lhue(120, "Mon ") + lhue(165, "Tue ") + lhue( 55, "Wed ") + lhue( 30, "Thu ") + lhue(285, "Fri ") + lhue( 0, "Sat ") + return +end + +procedure byday(p, q) + static hues + initial hues := [240, 120, 165, 55, 30, 285, 0] + + return hues[1 + ((4 + integer(\q.t)) % 7)] +end + + +# H: color segments by hour in the day (0 to 11, repeated) + +procedure hourlegend() + + lhue(240, "12 ") + lhue(290, "1 ") + lhue(350, "2 ") + lhue( 30, "3 ") + lhue( 80, "4 ") + lhue(150, "5 ") + lhue(210, "6 ") + lhue(270, "7 ") + lhue(330, "8 ") + lhue( 55, "9 ") + lhue(120, "10 ") + lhue(180, "11 ") + return +end + +procedure byhour(p, q) + local h + static hues + initial hues := [240, 290, 350, 30, 80, 150, 210, 270, 330, 55, 120, 180] + + h := integer(24 * (\q.t - integer(q.t))) | fail + return hues[1 + h % 12] +end + + +# M: color segments by minute of the hour, mod 10 + +procedure minutelegend() + local i + + every i := 0 to 9 do + lhue(huenum(i + 1), ":x" || i || " ") + return +end + +procedure byminute(p, q) + local t + + t := 24 * 30 * (\p.t + \q.t) | fail # time in minutes since epoch + return huenum(1 + integer(t) % 10) +end + + +# T: color segments by a time-of-day spectrum + +procedure timelegend() + + ltext("midnight") + lspectrum(600, 420, 13) + ltext("noon") + lspectrum(420, 240, 13) + ltext("midnight") + return +end + +procedure bytime(p, q) + + # green morning, yellow noon, red afternoon, blue night + return 600. - 360. * (\q.t - integer(q.t)) +end diff --git a/ipl/gprogs/trycolor.icn b/ipl/gprogs/trycolor.icn new file mode 100644 index 0000000..c74172f --- /dev/null +++ b/ipl/gprogs/trycolor.icn @@ -0,0 +1,96 @@ +############################################################################ +# +# File: trycolor.icn +# +# Subject: Program to investigate color specifications +# +# Author: Gregg M. Townsend +# +# Date: July 14, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# trycolor repeatedly reads a color specification from standard input +# and displays a disc of that color. A color specification may be in any +# of the forms accepted by Icon, for example: +# +# blue +# #ffedcb +# 50010,60422,8571 +# dark greenish blue +# +# Additionally, the leading '#' may be omitted from hexadecimal forms. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, graphics +# +############################################################################ + +link options +link optwindw +link graphics + +procedure main(args) + local win, gc, line, cval, mono, color, opts, m, w, h, l + local r, g, b, rr, gg, bb, x + + opts := options(args, winoptions()) + /opts["W"] := 300 + /opts["H"] := 300 + /opts["M"] := -1 + win := optwindow(opts, "cursor=off", "echo=off") + gc := Clone(win) + m := opts["M"] + w := opts["W"] + h := opts["H"] + l := WAttrib(win, "leading") + color := opts["F"] + mono := WAttrib(win, "depth") == "1" + write("gamma=", WAttrib(win, "gamma")) + repeat { + if *color > 0 then { + if Shade(gc, color | (color := "#" || color)) then { + EraseArea(gc) + FillArc(gc, m, m, w, h) + Fg(win, Contrast(win, color)) + cval := ColorValue(win, color) + cval ? { + r := tab(many(&digits)); move(1) + g := tab(many(&digits)); move(1) + b := tab(many(&digits)) + } + rr := hexv(r / 65536.0) + gg := hexv(g / 65536.0) + bb := hexv(b / 65536.0) + CenterString(win, m + w/2, m + h/2 - l, color) + CenterString(win, m + w/2, m + h/2, cval) + CenterString(win, m + w/2, m + h/2 + l, "#" || rr || gg || bb) + } + else + write("[failed]") + } + writes("> ") + line := read() | break + line ? { + tab(many(' \t')) + color := trim(tab(0)) + } + } +end + +procedure hexv(v) # two-hex-digit specification of v + static hextab + initial { + every put((hextab := []), !"0123456789ABCDEF" || !"0123456789ABCDEF") + } + return hextab [1 + integer(256 * v)] +end diff --git a/ipl/gprogs/tryfont.icn b/ipl/gprogs/tryfont.icn new file mode 100644 index 0000000..be55a0f --- /dev/null +++ b/ipl/gprogs/tryfont.icn @@ -0,0 +1,110 @@ +############################################################################ +# +# File: tryfont.icn +# +# Subject: Program to demonstrate X font rankings +# +# Author: Gregg M. Townsend +# +# Date: July 18, 1995 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# tryfont repeatedly reads a font specification from standard input +# and displays, with their scores, a windowfull of available fonts that +# best match that specification. The window can be resized when tryfont +# is paused at a prompt; the new size is used for the next list. +# +# Note that tryfont uses the library procedure BestFont() for ranking; +# this can differ from the rankings used by the Icon runtime system's +# font selection logic. +# +# tryfont can also be run in ASCII mode, without using X windows, by +# passing a file name as a command argument. The file should contain +# a list of X fonts, such as from the xlsfonts program. The number of +# fonts printed on standard output can be specified as a second argument. +# +# For details of font specifications, see BestFont(). +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, optwindw, xbfont, graphics +# +############################################################################ + + +link options +link optwindw +link xbfont +link graphics + + +procedure main(args) + if *args > 0 & args[1][1] ~== "-" then + filemode(args) + else + windowmode(args) +end + + +procedure filemode(args) + local fname, limit, f, fontlist, request, a + + fname := args[1] + limit := integer(args[2]) | 20 + f := open(fname) | stop("can't open ", fname) + every put(fontlist := [], !f) + repeat { + writes("> ") + request := trim(read()) | return + if *request = 0 then + next + every a := RankFonts(fontlist, request) \ limit do + write(right(a.val, 5), "\t", a.str) + write() + } +end + + +procedure windowmode(args) + local opts, win, fwin, request, a, h, y + + opts := options(args, winoptions()) + /opts["W"] := 900 + /opts["H"] := 300 + /opts["M"] := -1 + win := optwindow(opts, "cursor=off", "echo=off") + fwin := Clone(win) + + &error := 1 + WAttrib(win, "resize=on") + &error := 0 + + repeat { + writes("> ") + request := trim(read()) | return + if *request = 0 then + next + h := WAttrib(win, "height") + y := 0 + EraseArea(win) + every a := RankFonts(win, request) do { + Font(fwin, a.str) + y +:= WAttrib(fwin, "fheight") - WAttrib(fwin, "descent") + GotoXY(win, 10, y) + writes(win, right(a.val, 4), " ") + writes(fwin, a.str) + y +:= WAttrib(fwin, "descent") + if y >= h then + break + } + } +end diff --git a/ipl/gprogs/uix.icn b/ipl/gprogs/uix.icn new file mode 100644 index 0000000..8cf04d9 --- /dev/null +++ b/ipl/gprogs/uix.icn @@ -0,0 +1,223 @@ +############################################################################ +# +# File: uix.icn +# +# Subject: Program to translate user interfaces +# +# Author: Gregg M. Townsend +# +# Date: May 31, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# uix translates a user interface prototype or application +# built by xib, the old X-Icon Interface Builder, into a skeletal +# application of the form used by vib, the new Visual Interface +# Builder. The resulting file is a working application containing +# all the vidgets (buttons, sliders, etc.) from the input file but +# none of the user Icon code. This must be added manually. Some +# of the vidget sizes may be incorrect; load and save the file in +# vib to fix this. +# +# usage: uix [file] +# +# Input is read from the named file, or from standard input if +# none is specified. Output is written to standard output. +# +############################################################################ +# +# Requires: Version 9 +# +############################################################################ + +$define YOFF 78 # offset incorporated in y values by XIB + +$define FONT "lucidasanstypewriter-bold-12" # VIB Font + +record ob(t, c, v, x, y, w, h, l, s, n, i, j, k, etc) +# type callback var x,y,w,h lbl style number initval min max other + + +# main program + +procedure main(args) + local f, line, data, objs, recs, curr, o, fmt, r, c, v, i + + # open file, skip to data + if *args = 0 then + f := &input + else + f := open(args[1]) | stop(&progname, ": can't open ", args[1]) + while line := read(f) | stop(&progname, ": EOF hit before finding data") do + if match("# Session Code:", line) then break + + # read data + objs := [] # list of objects + curr := [] # fields of current object + while line := read(f) do { + data := line[3:0] + # in the following, special case lets Scrollbar consume Slider + if data[-5:0] == "_Obj:" & (*curr ~= 1 | *objs == 0) then + put(objs, curr := []) + put(curr, data) + } + close(f) + + # define interpretations + fmt := table() + fmt["Sizer"] := "txywh" + fmt["Button"] := "tcv.xywhl...sn.." + fmt["Check"] := "tcv.xywh.." + fmt["Text_Input"] := "tcv.xywh.lin.." + fmt["Scrollbar"] := "t.cnv.xywh.jkis...cnv.jkxywh..." + fmt["Slider"] := "tcnv.xywh.jkis..." + fmt["Line"] := "tcv...xywh....sn" + fmt["Rect"] := "tcv.xywhn.." + fmt["Message"] := "tcv.xywhl...." + fmt["Radio_Button"] := "tcv.xywh...n" + fmt["Menu"] := "tcv.xywhl..s.." + + # convert object lists into records + recs := [] # list of records + every o := !objs do { + r := ob() # create empty record + f := \fmt[o[1][1:-5]] | { # find appropriate format + write(&progname, ": vidget type ", o[1], " unrecognized") + next + } + f ? while c := move(1) do { # get next char from format + v := get(o) | "" # get next value, default "" + if c ~== "." then + r[c] := v # store in rec field named by format + } + adjust(r) # clean up special cases + r.etc := o # save leftovers in "etc" field + put(recs, r) # put record on list + } + + # write UI program + prologue() + write( + "#===<<vib:begin>>===\tmodify using vib; do not remove this marker line") + write("procedure ui(win, cbk)") + write("return vsetup(win, cbk,") + every output(!recs) # output spec for each line + write(" )") + write("end") + write("#===<<vib:end>>===\tend of section maintained by vib") +end + + +# adjust(r) -- clean up record fields including type-dependent cases + +procedure adjust(r) + /r.v := "" # default varname to "" not &null + \r.y -:= YOFF # subtract xib header from y value + r.t := r.t[1:-5] # chop "_Obj" off name + case r.t of { + "Sizer": { # Sizer (overall setup) vidget: + r.s := FONT # add font expected by VIB + } + "Line": { # Line vidget: + \r.h -:= YOFF # "height" is really 2nd y coordinate + } + "Text_Input": { # Text vidget: + r.t := "Text" # simplify name + r.l ||:= "\\\\=" || r.i # concatenate initial value + } + "Slider" | "Scrollbar": { # Slider, Scrollbar: + r.l := r.j || "," || r.k || "," || r.i # add bounds and init value + } + "Message": { # Message vidget: + r.t := "Label" # change name + } + "Radio_Button": { # Radio_Button vidget: + r.t := "Choice" # simplify name + } + } + return +end + + +# prologue() -- write boilerplate prologue to acual spec + +procedure prologue() +every write(![ + "# User interface specification translated to vib format by uix", + "# (Load and save this file once in vib to correct size information.)", + "#", + "# This is a working program that responds to vidget events by printing", + "# messages. Use a text editor to replace this skeletal program with your", + "# own code. Retain the vib section at the end and use vib to make any", + "# changes to the interface.", + "#", + "# When a callback is generated, but there is no callback procedure, a", + "# message is printed. Remove the vecho argument below to prevent this.", + "", + "link vsetup", + "", + "procedure main()", + " local vidgets", + "", + " vidgets := ui(, vecho)\t\t\t# set up vidgets", + " GetEvents(vidgets[\"root\"], QuitCheck)\t# enter event loop", + "end", + "", + "", + ""]) +end + + +# output(r) -- output one record in vib format + +procedure output(r) + if /r.t then + fail + writes(" [\"") + writes(r.v, ":", r.t, ":", r.s, ":", r.n, ":") + writes(r.x, ",", r.y, ",", r.w, ",", r.h, ":") + writes(r.l, "\",", r.c) + if r.t == "Menu" then + outmenu(r.etc) + else if *r.etc > 0 then { + writes(",\n [", image(get(r.etc))) + while writes(",", image(get(r.etc))) + writes("]") + } + write("],") + return +end + + +# outmenu(lst) -- output a list of menu entries + +procedure outmenu(lst) + local msize + + msize := get(lst) + if msize = 0 then + return + writes(",\n [") + outentry(lst) + every 2 to msize do { + writes(",") + outentry(lst) + } + writes("]") + return +end + + +# outentry(lst) -- output menu entry + +procedure outentry(lst) + writes(image(get(lst))) # output label + get(lst) # skip unused data + get(lst) + outmenu(lst) # output submenu (if any) + return +end diff --git a/ipl/gprogs/unitgenr.icn b/ipl/gprogs/unitgenr.icn new file mode 100644 index 0000000..53da108 --- /dev/null +++ b/ipl/gprogs/unitgenr.icn @@ -0,0 +1,103 @@ +############################################################################ +# +# File: unitgenr.icn +# +# Subject: Program to produce unit generators of patterna +# +# Author: Ralph E. Griswold +# +# Date: July 13, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# BLPs are read from standard input and their unit generators written +# to standard output. +# +# The following command line option is supported: +# +# -c assume complete repeats; default, do not +# +############################################################################ +# +# Links: factors, options, patutils, patxform +# +############################################################################ + +link factors +link options +link patutils +link patxform + +global switch + +procedure main(args) + local opts, oldpat, pattern + + opts := options(args, "c") + switch := if /opts["c"] then 1 else &null + + while oldpat := read() do { + every 1 to 10 do { # SAFETY! + pattern := rows2pat(unit(pat2rows(oldpat))) + if pattern == oldpat then break + oldpat := pattern + } + write(pattern) + } + +end + +procedure unit(grid) + + grid := grepeat(grid) + + grid := grepeat(protate(grid)) + + return protate(grid, -90) + +end + +procedure grepeat(grid) #: reduce grid to smallest repeat + local i, width, j, periods + + grid := copy(grid) + + periods := [] + + width := *grid[1] + + if /switch then { # assume no partial repeats + every i := 1 to *grid do + put(periods, xperiod(grid[i]) | width) + width >:= lcml ! periods + every i := 1 to *grid do + grid[i] := left(grid[i], width) + return grid + } + else { + every i := 1 to width do { + every j := 1 to *grid do { + grid[j] == extend(grid[j][1+:i], width) | break next + } + break + } + every j := 1 to *grid do + grid[j] := left(grid[j], i) + return grid + } + +end + +procedure xperiod(s) + local i + + every i := 1 | divisors(*s) do + if extend(s[1+:i], *s) == s then return i + + fail + +end diff --git a/ipl/gprogs/viewpane.icn b/ipl/gprogs/viewpane.icn new file mode 100644 index 0000000..e02a452 --- /dev/null +++ b/ipl/gprogs/viewpane.icn @@ -0,0 +1,195 @@ +############################################################################ +# +# File: viewpane.icn +# +# Subject: Program to view image through a "pane" +# +# Author: Ralph E. Griswold +# +# Date: November 27, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program loads images and uses scroll bars to pan over parts +# of an image that is larger than the viewing pane. +# +# This program is intended primarily as an example of a simple +# application with a visual interface. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: dialog, vsetup +# +############################################################################ + +link dialog +link vsetup + +global view_xoff, view_yoff # upper-left corner of pane +global view_width, view_height # view-pane dimensions +global x_fact, y_fact # image scaling values +global image_win # image window +global image_width, image_height # image dimensions +global xpos, ypos # upper-left corner of image +global hbar, vbar # scrolling vidgets + +procedure main() + local vidgets + + vidgets := ui() # set up interface + + view_xoff := vidgets["port"].ax + 1 # get pane information + view_yoff := vidgets["port"].ay + 1 + view_width := vidgets["port"].aw - 1 + view_height := vidgets["port"].ah - 1 + + hbar := vidgets["hbar"] # horizontal scroll bar + vbar := vidgets["vbar"] # vertical scroll bar + + GetEvents(vidgets["root"], , shortcuts) # enter event loop + +end + + +# Process event for the file menu. + +procedure file_cb(vidget, menu) + + case menu[1] of { + "open @O": image_open() + "quit @Q": exit() + } + + return + +end + +# Check for keyboard shortcuts. + +procedure shortcuts(e) + + case &meta & map(e) of { # fold case + "o": image_open() + "q": exit() + } + + return + +end + +# Open image file. + +procedure image_open() + + case OpenDialog() of { + "Okay": { + WClose(\image_win) + image_win := WOpen("image=" || dialog_value, "canvas=hidden") | { + Notice("Cannot open image file") + fail + } + setup_win(image_win) + return + } + "Cancel": fail + } + +end + +# Process event for horizontal scroll bar. + +procedure horiz_cb(vidget, val) + + if /image_win then return # don't do anything if no image + + xpos := val * x_fact + + copy_image() + + return + +end + +# Process event for vertical scroll bar. + +procedure vert_cb(vidget, val) + + if /image_win then return # don't do anything if no image + + ypos := val * y_fact + + copy_image() + + return + +end + +# Process event for "hide" button. + +procedure hide_cb(vidget, val) + + if /image_win then return # don't do anything if no image + + if val === 1 then + FillRectangle(view_xoff, view_yoff, view_width, view_height) + else copy_image() + + return + +end + +# Utility procedure for copying image. + +procedure copy_image() + + CopyArea(image_win, &window, xpos, ypos, view_width, view_height, + view_xoff, view_yoff) + + return + +end + +# Procedure to set up window. + +procedure setup_win(win) + + EraseArea(view_xoff, view_yoff, view_width, view_height) + image_width := real(WAttrib(win, "width")) + image_height := real(WAttrib(win, "height")) + x_fact := 1.0 - view_width / image_width # set up x and y factors + y_fact := 1.0 - view_height / image_height + x_fact <:= 0.0 + y_fact <:= 0.0 + x_fact *:= image_width + y_fact *:= image_height + VSet(hbar, 0.0) # reset the scroll bars + VSet(vbar, 0.0) + xpos := ypos := 0 + copy_image() # place image + + return + +end + +#===<<vib:begin>>=== modify using vib; do not remove this marker line +procedure ui(win, cbk) +return vsetup(win, cbk, + [":Sizer:lucidasanstypewriter-bold-12::0,0,455,410:View",], + ["file:Menu:pull::29,1,36,21:File",file_cb, + ["open @O","quit @Q"]], + ["hbar:Scrollbar:h:1:30,362,300,18:0.0,1.0,0.5",horiz_cb], + ["hide:Button:regular:1:382,60,45,20:Hide",hide_cb], + ["line:Line:solid:1:0,25,455,25:",], + ["port:Rect::1:30,60,300,300:",], + ["vbar:Scrollbar:v:1:332,60,18,300:0.0,1.0,0.5",vert_cb], + ) +end +#===<<vib:end>>=== end of section maintained by vib diff --git a/ipl/gprogs/vqueens.icn b/ipl/gprogs/vqueens.icn new file mode 100644 index 0000000..585a682 --- /dev/null +++ b/ipl/gprogs/vqueens.icn @@ -0,0 +1,222 @@ +############################################################################ +# +# File: vqueens.icn +# +# Subject: Program to display solutions to the n-queens problem +# +# Author: Ralph E. Griswold +# +# Date: January 5, 1997 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Adapted from a text-display version by Steve Wampler. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, wopen +# +############################################################################ + +link options +link wopen + +global solution +global black_queen, white_queen + +$define Edge 4 +$define Offset 40 +$define Size 44 + +global queens + +procedure main(args) + local i, opts, wsize, bqueen, wqueen + + opts := options(args,"n+") + queens := \opts["n"] | 8 + if queens <= 0 then stop("-n needs a positive numeric parameter") + wsize := queens * Size + 2 * Offset + + + WOpen("size=" || wsize || "," || wsize, "label=" || queens || + "-queens") | stop("*** cannot open window") + black_queen := WOpen("canvas=hidden", "size=41,41") | + stop("*** cannot open window for black queen") + white_queen := WOpen("canvas=hidden", "size=41,41") | + stop("*** cannot open window for white queen") + + DrawImage(black_queen, 0, 0, + "41,c1,_ + 66666666666666666666666666666666666666666_ + 66666666666666666666666666666666666666666_ + 66666666666666666666666666666666666666666_ + 66666666666664003666666663004666666666666_ + 66666666666650000466666640000566666666666_ + 66666666666640000366666630000466666666666_ + 66666666666660000566666650000666666666666_ + 66666666666665224666666664225666666666666_ + 66663346666666644666666664466666666433666_ + 66620004666666631666666661366666664000266_ + 66600002666666640666666660466666662000066_ + 66600003666666650466666640566666663000066_ + 66640026666666660166666610666666666200466_ + 66666651666666660046666400666666661566666_ + 66666662266666660026666200666666622666666_ + 66666666036666660004663000666666306666666_ + 66666666403666640000220000466663046666666_ + 66666666620266620000000000266620266666666_ + 66666666650002100000000000012000566666666_ + 66666666663000000000000000000003666666666_ + 66666666666000000000000000000006666666666_ + 66666666666300000000000000000036666666666_ + 66666666666500000000000000000056666666666_ + 66666666666610000000000000000166666666666_ + 66666666666630000000000000000366666666666_ + 66666666666652222222222222222566666666666_ + 66666666666664444444444444444666666666666_ + 66666666666640000000000000000466666666666_ + 66666666666651000000000000001566666666666_ + 66666666666664000000000000004666666666666_ + 66666666666651000000000000001566666666666_ + 66666666666640000000000000000466666666666_ + 66666666666664444444444444444666666666666_ + 66666666653222222222222222222223566666666_ + 66666666600000000000000000000000066666666_ + 66666666400000000000000000000000046666666_ + 66666666300000000000000000000000036666666_ + 66666666300000000000000000000000036666666_ + 66666666300000000000000000000000036666666_ + 66666666300000000000000000000000036666666_ + 66666666666666666666666666666666666666666_ + ") + + DrawImage(white_queen, 0, 0, + "41,c1,_ + 00000000000000000000000000000000000000000_ + 00000000000000000000000000000000000000000_ + 00000000000026630000000036620000000000000_ + 00000000000166662000000266661000000000000_ + 00000000000266663000000366662000000000000_ + 00000000000066661000000166660000000000000_ + 00000000000014420000000024410000000000000_ + 00033200000000220000000022000000002330000_ + 00466620000000350000000053000000026664000_ + 00666640000000260000000062000000046666000_ + 00666630000000162000000261000000036666000_ + 00266400000000065000000560000000004662000_ + 00000150000000066200002660000000051000000_ + 00000044000000066400004660000000440000000_ + 00000006300000066620036660000003600000000_ + 00000002630000266664466662000036200000000_ + 00000000464000466666666664000464000000000_ + 00000000166645666666666666546661000000000_ + 00000000036666666666666666666630000000000_ + 00000000006666666666666666666600000000000_ + 00000000003666666666666666666300000000000_ + 00000000001666666666666666666100000000000_ + 00000000000566666666666666665000000000000_ + 00000000000366666666666666663000000000000_ + 00000000000144444444444444441000000000000_ + 00000000000022222222222222220000000000000_ + 00000000000266666666666666662000000000000_ + 00000000000156666666666666651000000000000_ + 00000000000026666666666666620000000000000_ + 00000000000156666666666666651000000000000_ + 00000000000266666666666666662000000000000_ + 00000000000022222222222222220000000000000_ + 00000000134444444444444444444431000000000_ + 00000000666666666666666666666666000000000_ + 00000002666666666666666666666666200000000_ + 00000003666666666666666666666666300000000_ + 00000003666666666666666666666666300000000_ + 00000003666666666666666666666666300000000_ + 00000003666666666666666666666666300000000_ + 00000000000000000000000000000000000000000_ + 00000000000000000000000000000000000000000_ + ") + + DrawBoard() + + solution := list(queens) # ... and a list of column solutions + + every q(1) # start by placing queen in first column + + until WQuit() + +end + +# q(c) - place a queen in column c. +# +procedure q(c) + local r + static up, down, rows + initial { + up := list(2 * queens - 1, 0) + down := list(2 * queens - 1, 0) + rows := list(queens, 0) + } + every 0 = rows[r := 1 to queens] = up[queens+r-c] = down[r+c-1] & + rows[r] <- up[queens+r-c] <- down[r+c-1] <- 1 do { + solution[c] := r # record placement. + if c = queens then show() + else q(c + 1) # try to place next queen. + } +end + +# show the solution on a chess board. +# +procedure show() + local i, j, queen + + every i := 1 to *solution do { + j := solution[i] + queen := if (i + j) % 2 = 0 then black_queen else white_queen + CopyArea(queen, &window, , , , , Offset + (i - 1) * Size + 1, + Offset + (j - 1) * Size + 1) + } + + WDelay(500) + + while *Pending() > 0 do { + case Event() of { + "q": exit() + "p": until Event() === "c" + } + } + + every i := 1 to *solution do { + j := solution[i] + if (i + j) % 2 = 1 then Fg("black") else Fg("white") + FillRectangle(Offset + (i - 1) * Size, Offset + (j - 1) * Size, + Size, Size) + } + + return + +end + +procedure DrawBoard() + local i, j + + every i := 0 to queens - 1 do + every j := 0 to queens - 1 do + if (i + j) % 2 = 1 then + FillRectangle(Offset + i * Size, Offset + j * Size, + Size, Size) + DrawRectangle(Offset - 1, Offset - 1, queens * Size + 1, + queens * Size + 1) + DrawRectangle(Offset - Edge - 1, Offset - Edge - 1, + queens * Size + 2 * Edge + 1, queens * Size + 2 * Edge + 1) + + return + +end diff --git a/ipl/gprogs/webimage.icn b/ipl/gprogs/webimage.icn new file mode 100644 index 0000000..2b913fc --- /dev/null +++ b/ipl/gprogs/webimage.icn @@ -0,0 +1,84 @@ +############################################################################ +# +# File: webimage.icn +# +# Subject: Program to produce Web page for image files +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes the names of image files on the command line and +# writes a Web page that embeds each image. +# +# The following options are supported: +# +# -a s alignment, default "bottom" +# -t s title for page; default "untitled" +# -n include file names; default no names +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: options, wopen +# +############################################################################ + +link options +link wopen + +record dim(w, h) + +procedure main(args) + local name, opts, title, dim, align, names + + opts := options(args, "t:a:n") + title := \opts["t"] | "untitled" + align := \opts["a"] | "bottom" + names := opts["n"] + + write("<html><head><title>", title, "</title></head><body>") + + every name := !args do { + dim := image_size(name) | { + write(&errout, "*** cannot open image file ", image(name)) + next + } + write( + if \names then name else "", + "<p><img src=\"", + name, + "\" width=\"", + dim.w, + "\" height=\"", + dim.h, + "\" align=\"", + align, + "\"></p>" + ) + } + write("</body></html>") + +end + +procedure image_size(name) #: size of GIF file + local win, size + + win := WOpen("canvas=hidden", "image=" || name) | fail + + size := dim(WAttrib(win, "width"), WAttrib(win, "height")) + + WClose(win) + + return size + +end diff --git a/ipl/gprogs/wevents.icn b/ipl/gprogs/wevents.icn new file mode 100644 index 0000000..383feeb --- /dev/null +++ b/ipl/gprogs/wevents.icn @@ -0,0 +1,140 @@ +############################################################################ +# +# File: wevents.icn +# +# Subject: Program to report Icon window events +# +# Author: Gregg M. Townsend +# +# Date: August 4, 1998 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# wevents reports all the events delivered to an Icon window. +# Each event produces a single line of output. The program terminates +# after receiving and reporting a ^C, ^D, or DELETE key event. +# +# Each event is reported both in Icon terms and in terms of its +# internal representation. The output fields on each line are: +# +# &interval (interval since previous event, in milliseconds) +# &control, &meta, &shift (modifier keys: c, m, or s if pressed) +# event returned by Event: keyword name, if any, or else image +# &x, &y (usually coordinates, but new size for resize event) +# +# image() of the first value on the event queue +# hex dump of the second value (modifier flags and x coordinate) +# hex dump of the third value (encoded interval and y coordinate) +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: hexcvt, options, optwindw +# +############################################################################ + +link hexcvt, options, optwindw + +$include "keysyms.icn" + +procedure main(args) + local w, q, e, xhex, yhex, eimage + + w := optwindow(options(args, winoptions())) + WAttrib(w, "resize=on") + + repeat { + q := Pending(Active()) # wait until event is queued + eimage := right(image(q[1]), 10) | "******" + xhex := hexstring(q[2], 8) | "********" + yhex := hexstring(q[3], 8) | "********" + e := Event(w) + write( + r(&interval, 5), " ", + if &control then "c" else "-", + if &meta then "m" else "-", + if &shift then "s" else "-", + " ", right(evname(e), 10), + " @", r(&x, 4), ",", l(&y, 6), + eimage, " ", + left(xhex, 4), " ", right(xhex, 4), " ", + left(yhex, 4), " ", right(yhex, 4), + ) + if e === ("\^C" | "\^D" | "\177") then + break + if e === &resize & &x < 0 & &y < 0 then + break + } +end + + +# evname(e) -- translate e into text representation + +procedure evname(e) + return case e of { + &lpress: "&lpress" + &mpress: "&mpress" + &rpress: "&rpress" + &lrelease: "&lrelease" + &mrelease: "&mrelease" + &rrelease: "&rrelease" + &ldrag: "&ldrag" + &mdrag: "&mdrag" + &rdrag: "&rdrag" + &resize: "&resize" + Key_PrSc: "Key_PrSc" + Key_ScrollLock: "Key_ScrollLock" + Key_Pause: "Key_Pause" + Key_Insert: "Key_Insert" + Key_Home: "Key_Home" + Key_PgUp: "Key_PgUp" + Key_End: "Key_End" + Key_PgDn: "Key_PgDn" + Key_Left: "Key_Left" + Key_Up: "Key_Up" + Key_Right: "Key_Right" + Key_Down: "Key_Down" + Key_F1: "Key_F1" + Key_F2: "Key_F2" + Key_F3: "Key_F3" + Key_F4: "Key_F4" + Key_F5: "Key_F5" + Key_F6: "Key_F6" + Key_F7: "Key_F7" + Key_F8: "Key_F8" + Key_F9: "Key_F9" + Key_F10: "Key_F10" + Key_F11: "Key_F11" + Key_F12: "Key_F12" + default: image(e) + } +end + + +# r(v, n) -- right-justify image of v in at least n characters + +procedure r(v, n) + local s + s := image(v) + if *s < n then + s := right(s, n) + return s +end + + +# l(v, n) -- left-justify image of v in at least n characters + +procedure l(v, n) + local s + s := image(v) + if *s < n then + s := left(s, n) + return s +end diff --git a/ipl/gprogs/wheel.icn b/ipl/gprogs/wheel.icn new file mode 100644 index 0000000..8d1eec1 --- /dev/null +++ b/ipl/gprogs/wheel.icn @@ -0,0 +1,62 @@ +############################################################################ +# +# File: wheel.icn +# +# Subject: Program to show wheel of colors +# +# Author: Gregg M. Townsend +# +# Date: November 14, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# wheel displays a disk made of randomly colored sectors. In addition +# to the usual window options, the number of sectors may be given. +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, random +# +############################################################################ + +link graphics +link random + +$define BevelWidth 2 +$define WindowMargin 10 + +procedure main(args) + local win, gc, w, h, m, a, da, n, ov, i, t + + win := Window("size=400,400", args) + n := integer(args[1]) | 18 + + m := WindowMargin + w := WAttrib("width") - 2 * m + h := WAttrib("height") - 2 * m + randomize() + + gc := [] + every 1 to n do + put(gc, Shade(Clone(win), ?65535 || "," || ?65535 || "," || ?65535)) + if *gc = 0 then + stop("can't allocate any colors") + if n >:= *gc then + write(&errout, "using only ", n, " colors") + + da := 2 * &pi / n # change in angle + a := -&pi / 2 - da # current angle + ov := &pi / 1000 # small overlap + + every i := 1 to n do + FillArc(gc[i], m, m, w, h, a +:= da, da + ov) + WDone(win) +end diff --git a/ipl/gprogs/wif2isd.icn b/ipl/gprogs/wif2isd.icn new file mode 100644 index 0000000..a5a05a0 --- /dev/null +++ b/ipl/gprogs/wif2isd.icn @@ -0,0 +1,71 @@ +############################################################################ +# +# File: wif2isd.icn +# +# Subject: Program to convert WIFs to ISDs +# +# Author: Ralph E. Griswold +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# The following option is supported: +# +# -n s name; default "untitled" +# +# Note: The output is an xencoded ISD. +# +# There is a problem where there is treadling with multiple treadles +# and no liftplan. *Presumably* that treadling can be used like a +# liftplan, but without, necessarily, a direct tie-up. This problem +# problem has not been addressed yet. +# +# If there is a liftplan, then a direct tie-up is implied by the +# wording in the WIF documentation. However, that's in the interpretation +# of the draft. The tie-up produced here is the one given in the +# +# If there is a liftplan and a treadling with multiple treadles, +# the treadling is ignored. +# +# Also not handled is the possibility of multiple shafts per thread. +# This could be dealt with as for the liftplan. The idea is that +# instead of a threading corresponding to a single shaft, there are +# some number of different shaft patterns, like there are liftplan +# patterns. +# +# The liftplan is represented as concatenated rows of shaft patterns in +# the order they first appear. Thus, the symbols used for them can be +# reconstructed with the ISD is processed. +# +# This program does not attempt to detect or correct errors in WIFs, +# but it does try to work around some common problems. +# +############################################################################ +# +# Links: options, wifisd +# +############################################################################ + +link options +link wifisd + +global data_default +global data_entries +global sections +global wif + +procedure main(args) + local opts, title, palette + + opts := options(args, "n:") + + title := \opts["n"] | "untitled" + + wif2isd(&input, title) + +end diff --git a/ipl/gprogs/wifs2pdb.icn b/ipl/gprogs/wifs2pdb.icn new file mode 100644 index 0000000..a3dc896 --- /dev/null +++ b/ipl/gprogs/wifs2pdb.icn @@ -0,0 +1,84 @@ +############################################################################ +# +# File: wifs2pdb.icn +# +# Subject: Program to create palette database from WIFs +# +# Author: Ralph E. Griswold +# +# Date: April 15, 2000 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This is a crude version; it does not bother with actually parsing WIF +# files and it assumes a color range of 2^16. +# +############################################################################ +# +# Links: basename, palettes, xcode +# +############################################################################ + +link basename +link palettes +link xcode + +global PDB_ + +procedure main(args) + local file, wifname, input, clist, line, range, i + + every file := !args do { + wifname := basename(file, ".wif") + input := open(file) | { + write(&errout, "*** cannot open ", image(file)) + next + } + clist := [] + range := &null + while line := trim(map(read(input))) do { + if line == "[color table]" then { + while line := trim(read(input)) do { + if *line = 0 then break + line ?:= { + if ="[" then break + tab(upto('=') + 1) + tab(0) + } + put(clist, line) + } + } + else if line == "[color palette]" then { + while line := trim(map(read(input))) do { + if *line = 0 then break + line ? { + if ="[" then break + else if ="range=" then { + tab(upto(',') + 1) + range := tab(0) + 1 + break + } + } + } + } + } + close(input) + + if (\range ~= 65536) then { # adjust color values + every i := 1 to *clist do + clist[i] := color_range(clist[i], range) | { + write(&errout, "*** bad color specification") + break break + } + } + makepalette(wifname, clist) | + write(&errout, "*** cannot make palette for ", image(wifname)) + } + + xencode(PDB_, &output) + +end diff --git a/ipl/gprogs/xbm2pat.icn b/ipl/gprogs/xbm2pat.icn new file mode 100644 index 0000000..c8dc413 --- /dev/null +++ b/ipl/gprogs/xbm2pat.icn @@ -0,0 +1,36 @@ +############################################################################ +# +# File: xbm2pat.icn +# +# Subject: Program to convert XBM file to pattern specification +# +# Author: Ralph E. Griswold +# +# Date: August 14, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program converts an XBM file to a pattern specification. +# +############################################################################ +# +# Links: patutils +# +############################################################################ + +link patutils + +procedure main(args) + local input, rlist + + input := open(args[1]) | stop("*** cannot open image file") + + rlist := [] + every put(rlist, xbm2rows(input)) + write(rows2pat(rlist)," # ", args[1]) + +end diff --git a/ipl/gprogs/xformpat.icn b/ipl/gprogs/xformpat.icn new file mode 100644 index 0000000..1c24735 --- /dev/null +++ b/ipl/gprogs/xformpat.icn @@ -0,0 +1,52 @@ +############################################################################ +# +# File: xformpat.icn +# +# Subject: Program to apply transformation to patterns +# +# Author: Ralph E. Griswold +# +# Date: August 12, 1993 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program takes patterns from standard input and applies a +# transformation to each one, writing the results to standard output. +# The transformation to be applied is given in terms of command-line +# arguments, with the transformation first, followed by any arguments, +# as in +# +# xformpat center 32 32 +# +# which would attempt to produce a 32x32 centered pattern from each +# pattern in standard input. +# +# Warning: Some transformations can fail. In cae of failure, no +# pattern is written. +# +############################################################################ +# +# Links: patxform +# +############################################################################ + +invocable all + +link patxform + +procedure main(args) + local xform, rows + + xform := proc("p" || args[1]) | stop("** invalid transformation") + + while rows := pat2rows(readpatt()) do { + get(args) # a trick here; there's always an extra + push(args, rows) + write(rows2pat(xform ! args)) + } + +end diff --git a/ipl/gprogs/xgamma.icn b/ipl/gprogs/xgamma.icn new file mode 100644 index 0000000..54eca83 --- /dev/null +++ b/ipl/gprogs/xgamma.icn @@ -0,0 +1,133 @@ +############################################################################ +# +# File: xgamma.icn +# +# Subject: Program to configure X color correction +# +# Author: Gregg M. Townsend +# +# Date: November 14, 1994 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Xgamma sets the root window properties that provide device-independent +# color under X windows. Icon derives the default value of the "gamma" +# attribute from these properties. +# +# Ideally, color properties would be set automatically based on +# specifications provided by the manufacturer of the monitor. +# Lacking such specifications, xgamma synthesizes intensity ramps +# for an ideal monitor characterized by a given gamma value. +# +# The phosphor colors, which must also be set, are set to those of a +# Sony Trinitron monitor based on values from the X11R5 distribution. +# +# There are three ways to call xgamma: +# +# xgamma m.n set color properties using gamma value m.n +# xgamma none remove color properties +# xgamma report gamma attribute inferred by Icon +# +# A pipe to "xcmsdb" is opened, so that program must be in the current +# search path. +# +# The default gamma attribute calculated by Icon does not always exactly +# match the value set by xgamma. The reason for this is unclear. +# +############################################################################ +# +# Requires: Version 9 graphics under X11R5 +# +############################################################################ +# +# Links: wopen +# +############################################################################ + +link wopen + +global ofile + +procedure main(args) + local gamma + + if *args = 0 then { + WOpen("canvas=hidden", "size=200,100") | stop("can't open window") + write(left(WAttrib("gamma") + 0.005, 4)) + return + } + + if map(args[1]) == ("none" | "off" | "remove") then { + system("xcmsdb -remove") + return + } + + gamma := real(args[1]) | 2.5 + ofile := open("xcmsdb", "wp") | stop("can't open pipe to xcmsdb") + + write(ofile, "SCREENDATA_BEGIN 0.3") + header() + matrices() + ramps(gamma) + write(ofile, ) + write(ofile, "SCREENDATA_END") +end + + +procedure header() + every write(ofile, ![ + "", + " NAME Unknown monitor", + " PART_NUMBER 3", + " MODEL Unknown", + " SCREEN_CLASS VIDEO_RGB", + " REVISION 2.0", + ]) +end + + +procedure matrices() + # Trinitron specs from X11R5 contrib/clients/xcrtca/monitors + every write(ofile, " ", \![ + "COLORIMETRIC_BEGIN", + " XYZtoRGB_MATRIX_BEGIN", + " 3.061645878834450 -1.278267953801873 -0.444951165661258", + " -1.032702121385028 1.976844500877421 0.008133037520752", + " 0.057063919003669 -0.199057800043321 0.779596768525705", + " XYZtoRGB_MATRIX_END", + " RGBtoXYZ_MATRIX_BEGIN", + " 0.422396751969335 0.297093836421011 0.237981555762915", + " 0.220555266059938 0.660453956058605 0.118990777881458", + " 0.025397273061447 0.146890261130091 1.295677359153649", + " RGBtoXYZ_MATRIX_END", + "COLORIMETRIC_END", + ]) +end + + +procedure ramps(gamma) + write(ofile, " INTENSITY_PROFILE_BEGIN 0 3") + every hue("RED" | "GREEN" | "BLUE", gamma) + write(ofile, " INTENSITY_PROFILE_END") +end + + +procedure hue(c, gamma) + local i, x, v + static hextab + initial every put((hextab := []), !"0123456789abcdef" || !"0123456789abcdef") + + write(ofile, " INTENSITY_TBL_BEGIN ", c, " 256") + every i := 0 to 255 do { + x := hextab[i + 1] + v := (i / 255.0) ^ gamma + if v < 0.0001 then # avoid "e" notation + v := 0.0 + write(ofile, " 0x", x, x, " ", left(v, 8, "0")) + } + write(ofile, " INTENSITY_TBL_END") +end diff --git a/ipl/gprogs/xpmtoims.icn b/ipl/gprogs/xpmtoims.icn new file mode 100644 index 0000000..2b4c1be --- /dev/null +++ b/ipl/gprogs/xpmtoims.icn @@ -0,0 +1,102 @@ +############################################################################ +# +# File: xpmtoims.icn +# +# Subject: Program to make Icon images from XPM files +# +# Author: Gregg M. Townsend +# +# Date: May 23, 1996 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Usage: xpmtoims [-d] [-gn | -cn] [file...] +# +# Xpmtoims reads XPM files and writes Icon image strings. +# -cn or -gn selects the color palette used; -c1 is the default. +# If -d is given, each image is displayed in a window after conversion. +# +# Output is a file of Icon source code suitable for use via $include. +# Each image is a string constant with a comment. +# Multiple images are separated by commas. +# +# (A window is always required, whether or not anything is displayed, +# so that the XPM colors can be converted by the window system.) +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: graphics, imscolor, options +# +############################################################################ + +link graphics, imscolor, options + +global opts, pal, nwritten + +procedure main(args) + local fname, f + + # Open the window and process options. + Window("size=100,20", args) + opts := options(args, "dg+c+") + pal := ("c" || \opts["c"]) | ("g" || \opts["g"]) | "c1" + PaletteChars(pal) | stop("invalid palette ", pal) + ColorValue("navy") | + write(&errout, "warning: no X color names, conversion is risky") + + # Convert the file. + nwritten := 0 + if *args = 0 then + dofile(&input, "[stdin]") + else + while fname := get(args) do + if f := open(fname) then { + dofile(f, fname) + close(f) + } + else { + write(&errout, fname, ": can't open") + } +end + + +# dofile(f, fname) -- process one file. + +procedure dofile(f, fname) + local s, e + + # Convert the file + s := XPMImage(f, pal) | { + write(&errout, fname, ": cannot decode") + return + } + + # Add spacing if this isn't the first image. + if (nwritten +:= 1) > 1 then + write(",\n") + + # Write the image. + write("# xpmtoims -", pal, " ", fname) + imswrite(, s) + flush(&output) + + # If requested, display the image. + if \opts["d"] then { + WAttrib("width=" || imswidth(s), "height=" || imsheight(s)) + EraseArea(0, 0) + DrawImage(0, 0, s) + while e := Event() do case e of { + QuitEvents(): exit() # quit on "q" etc + !" \t\r\n": break # continue on "\r" etc + } + } + return +end diff --git a/ipl/gprogs/zoomtile.icn b/ipl/gprogs/zoomtile.icn new file mode 100644 index 0000000..1489660 --- /dev/null +++ b/ipl/gprogs/zoomtile.icn @@ -0,0 +1,70 @@ +############################################################################ +# +# File: zoomtile.icn +# +# Subject: Program to show a tile magnified +# +# Author: Ralph E. Griswold +# +# Date: June 28, 2002 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# This program provides an optionally magnified view of a tile. +# +# File names are given on command line. Image files are written to +# <basename>_zoom.gif. +# +# Options are: +# +# -z i zoom factor, default 8 +# -g provide grid; only supported if zoom factor > 2 +# +############################################################################ +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Links: basename, options, patutils, win +# +############################################################################ + +link basename +link options +link patutils +link win + +procedure main(args) + local i, x, y, opts, magnif, pattern, dims, row, pixel, width, height, glist + local name, input + + opts := options(args, "z+g") + magnif := \opts["z"] | 8 + + every name := !args do { + input := open(name) | stop("Cannot open ", name) + pattern := readpatt(input) | stop("*** no tile specification") + close(input) + dims := tiledim(pattern) + width := magnif * dims.w + height := magnif * dims.h + win(width, height) + glist := [] + if \opts["g"] & (magnif > 2) then { + every y := 0 to height by magnif do + DrawLine(0, y, width, y) + every x := 0 to width by magnif do + DrawLine(x, 0, x, height) + } + DrawTile(0, 0, pattern, , magnif) + WriteImage(basename(name, ".blp") || "_zoom.gif") + WClose(&window) + &window := &null + } + +end |