############################################################################ # # File: tieedit.icn # # Subject: Procedures to create and edit binary arrays # # Authors: Ralph E. Griswold and Gregg M. Townsend # # Date: January 19, 2002 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # This package 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: interact, patxform, vdialog, vsetup, dialog, wopen # ############################################################################ link interact link patxform link vdialog link vsetup link dialog link wopen global cellsize global flip_horiz # icon for horizontal flip global flip_left # icon for left flip global flip_right # icon for right flip global flip_vert # icon for vertical flip global grid_height global grid_pane global grid_root global grid_rows global grid_state global grid_window global grid_width global grid_vidgets global hbits # number of bits horizontally global hi_horiz # highlighted icon for h-flip 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_rot_180 # highlighted icon for 180 rot global hi_rot_90 # highlighted icon for 90-rot global hi_rot_m90 # highlighted icon for -90 rot global hi_vert # highlighted icon for v-flip global ident # icon for identity global maxsize # maximum grid dimensions global mode # pattern/tile display mode global old_pat # old pattern for undo global rotate_180 # icon for 180-degree rotation global rotate_90 # icon for 90-degree rotation global rotate_m90 # icon for -90-degree rotation global subservient # application status global sym_image_current # current drawing images global sym_image_next # next drawing images global sym_state # drawing state global symmet_xpos global symmet_yoff global symmetries # general symmetry state global tile_touched # tile modification switch global vbits # number of bits veritcally global xform_xpos global xform_ypos $define MaxCell 24 # maximum size of grid cell $define IconSize 16 # size of button icons $define MaxPatt 32 $define InfoLength 40 # length of lines in info box record pattrec(tile) procedure copy_tile() local output output := open("/tmp/tieclip", "w") | { Notice("Cannot copy tile.") fail } write(output, rows2pat(grid_rows)) close(output) return end # draw editing grid procedure grid() local x, y EraseArea(grid_pane) every x := 0 to hbits * cellsize by cellsize do DrawLine(grid_pane, x, 0, x, vbits * cellsize) every y := 0 to vbits * cellsize by cellsize do DrawLine(grid_pane, 0, y, hbits * cellsize, y) return end # editing grid procedure grid_cb(vidget, e) local x, y, i, j static xpos, ypos initial { xpos := grid_vidgets["grid"].ax ypos := grid_vidgets["grid"].ay } if e === (&lpress | &rpress | &ldrag | &rdrag) then { j := (&x - xpos) / cellsize i := (&y - ypos) / 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 # file menu procedure grid_file_cb(vidget, menu) return case menu[1] of { "read @R" : read_tile() "open @O" : open_gif() "ims @M" : open_ims() "write @W" : write_tile() "copy @C" : copy_tile() "paste @P" : paste_tile() "quit @Q" : return_tile() "save @S" : save_image() } return end procedure grid_init() local e, i, j, x, y, v, h, input, window_save, atts local shift_up, shift_left, shift_right, shift_down, pixmap local clear, invert, scramble, trim, enlarge, resize, crop symmetries := 0 # initially no symmetries sym_state := [ # initially no symmetries [1, -1, -1, -1], [-1, -1, -1, -1] ] tile_touched := &null # Set up vidgets window_save := &window # save current subject window &window := &null # clear for new subject atts := grid_ui_atts() put(atts, "canvas=hidden") (WOpen ! atts) | stop("*** can't open drawdown editor window") grid_vidgets := grid_ui() grid_window := &window &window := window_save # restore previous subject window grid_root := grid_vidgets["root"] xform_xpos := grid_vidgets["xform"].ux xform_ypos := grid_vidgets["xform"].uy grid_width := grid_vidgets["grid"].uw grid_height := grid_vidgets["grid"].uh maxsize := grid_width / 3 grid_pane := Clone(grid_window, "bg=white", "dx=" || grid_vidgets["grid"].ax, "dy=" || grid_vidgets["grid"].ay) Clip(grid_pane, 0, 0, grid_width, grid_height) symmet_xpos := grid_vidgets["symregion"].ux symmet_yoff := grid_vidgets["symregion"].uy 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" 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] ] # now place the images place(xform_xpos, xform_ypos, 1, 0, shift_up) place(xform_xpos, xform_ypos, 0, 1, shift_left) place(xform_xpos, xform_ypos, 2, 1, shift_right) place(xform_xpos, xform_ypos, 1, 2, shift_down) place(xform_xpos, xform_ypos, 0, 4, flip_right) place(xform_xpos, xform_ypos, 0, 5, flip_left) place(xform_xpos, xform_ypos, 1, 4, flip_vert) place(xform_xpos, xform_ypos, 1, 5, flip_horiz) place(xform_xpos, xform_ypos, 0, 7, rotate_90) place(xform_xpos, xform_ypos, 0, 8, rotate_m90) place(xform_xpos, xform_ypos, 1, 7, rotate_180) place(xform_xpos, xform_ypos, 0, 10, clear) place(xform_xpos, xform_ypos, 1, 10, invert) place(xform_xpos, xform_ypos, 2, 10, scramble) place(xform_xpos, xform_ypos, 0, 12, trim) place(xform_xpos, xform_ypos, 1, 12, enlarge) place(xform_xpos, xform_ypos, 2, 12, resize) place(xform_xpos, xform_ypos, 0, 14, crop) place(symmet_xpos, symmet_yoff, 0, 0, hi_ident) place(symmet_xpos, symmet_yoff, 1, 0, rotate_90) place(symmet_xpos, symmet_yoff, 2, 0, rotate_m90) place(symmet_xpos, symmet_yoff, 3, 0, rotate_180) place(symmet_xpos, symmet_yoff, 0, 1, flip_right) place(symmet_xpos, symmet_yoff, 1, 1, flip_left) place(symmet_xpos, symmet_yoff, 2, 1, flip_vert) place(symmet_xpos, symmet_yoff, 3, 1, flip_horiz) VSetState(grid_vidgets["symstate"], "none ") return end # keyboard shortcuts procedure grid_shortcuts(e) if (e === "\r") & \subservient then return_tile() # subservient role if &meta then case map(e) of { "0" : read_rows() "1" : write_rows() "c" : copy_tile() "i" : tile_info() "m" : open_ims() "n" : new_tile() "o" : open_gif() "p" : paste_tile() "q" : return_tile() "r" : read_tile() "s" : save_image() "z" : undo_xform() "w" : write_tile() } 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 procedure new_tile() case Dialog("New:", ["height", "width"], [*grid_rows, *grid_rows[1]], 3, ["Okay", "Cancel"]) of { "Cancel" : fail "Okay" : { icheck(dialog_value) | fail grid_rows := list(dialog_value[1], repl("0", dialog_value[2])) tile_touched := 1 return setup() } } return end procedure open_gif() local win, ims repeat { if OpenDialog("Open image:") == "Cancel" then fail win := WOpen("canvas=hidden", "image=" || dialog_value) | { Notice("Cannot open image.") next } ims := Capture(win, "g2") WClose(win) setup_ims(ims) return } end procedure open_ims() local ims, input repeat { if OpenDialog("Open ims:") == "Cancel" then fail input := open(dialog_value) | { Notice("Cannot open ims file.") next } ims := read(input) close(input) setup_ims(ims) return } end procedure setup_ims(ims) local width grid_rows := [] ims ? { width := tab(upto(',')) while tab(upto(',') + 1) # while put(grid_rows, map(move(width), "01", "10")) while put(grid_rows, move(width)) } setup() return end procedure paste_tile() local input, tile input := open("/tmp/tieclip") | { Notice("Cannot paste tie-up file.") fail } tile := read_pattern(input) | { Notice("Cannot process matrix.") close(input) fail } close(input) grid_rows := pat2rows(tile.tile) return setup() end # place icon procedure place(xoff, yoff, col, row, pattern) DrawImage(grid_window, xoff + col * IconSize, yoff + row * IconSize, pattern) return 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 pattern to tile list procedure read_tile() local input, tile static file, line initial line := "1" repeat { if TextDialog("Read tile:", ["file", "line"], [file, line], [60, 4]) == "Cancel" then fail input := open(dialog_value[1]) | { Notice("Cannot open file.") next } file := dialog_value[1] line := (0 < integer(dialog_value[2])) every 1 to line - 1 do read(input) | { Notice("Not that many lines in file.") close(input) next } tile := read_pattern(input) | { Notice("Cannot process matrix.") close(input) next } close(input) grid_rows := pat2rows(tile.tile) return setup() } end # read and add rows to tile list procedure read_rows() local input static file repeat { if OpenDialog("Read rows:") == "Cancel" then fail input := open(dialog_value) | { Notice("Cannot open file.") next } file := dialog_value grid_rows := [] while put(grid_rows, read(input)) close(input) return setup() } end procedure return_tile() grid_state := "Done" return end procedure save_image() snapshot(grid_pane) return end # set bits of tile procedure setbit(i, j, c) local x, y, xu, yu, xv, yv, xt, yt, action static xpos, ypos initial { xpos := grid_vidgets["grid"].ax ypos := grid_vidgets["grid"].ay } if (symmetries = 0) & (grid_rows[i + 1, j + 1] == c) then return # optimization x := j * cellsize + 1 # the selected cell itself y := i * cellsize + 1 xt := i * cellsize + 1 yt := j * cellsize + 1 i +:= 1 # computational convenience j +:= 1 xu := (hbits - j) * cellsize + 1 # opposite cells yu := (vbits - i) * cellsize + 1 xv := (hbits - i) * cellsize + 1 yv := (vbits - j) * cellsize + 1 action := if c = 1 then FillRectangle else EraseArea if sym_state[1, 1] = 1 then { # cell itself grid_rows[i, j] := c action(grid_pane, x, y, cellsize - 1, cellsize - 1) } if sym_state[1, 2] = 1 then { # 90 degrees if grid_rows[j, -i] := c then # may be out of bounds action(grid_pane, xv, yt, cellsize - 1, cellsize - 1) } if sym_state[1, 3] = 1 then { # -90 degrees if grid_rows[-j, i] := c then # may be out of bounds action(grid_pane, xt, yv, cellsize - 1, cellsize - 1) } if sym_state[1, 4] = 1 then { # 180 degrees grid_rows[-i, -j] := c action(grid_pane, xu, yu, cellsize - 1, cellsize - 1) } if sym_state[2, 1] = 1 then { # left diagonal if grid_rows[j, i] := c then # may be out of bounds action(grid_pane, xt, yt, cellsize - 1, cellsize - 1) } if sym_state[2, 2] = 1 then { # right diagonal if grid_rows[-j, -i] := c then # may be out of bounds action(grid_pane, xv, yv, cellsize - 1, cellsize - 1) } if sym_state[2, 3] = 1 then { # vertical grid_rows[-i, j] := c action(grid_pane, x, yu, cellsize - 1, cellsize - 1) } if sym_state[2, 4] = 1 then { # horizontal grid_rows[i, -j] := c action(grid_pane, xu, y, cellsize - 1, cellsize - 1) } return end # set up editing grid and view area procedure setup() local i, j hbits := *grid_rows[1] vbits := *grid_rows if (hbits | vbits) > maxsize 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 >:= grid_width / (vbits + 4) cellsize >:= grid_height / (hbits + 4) grid() every i := 1 to hbits do every j := 1 to vbits do if grid_rows[j, i] == "1" then FillRectangle(grid_pane, (i - 1) * cellsize, (j - 1) * cellsize, cellsize, cellsize) return end procedure symstate_cb(vidget, value) local row, col # Note: the blanks at the end of these radio-button labels are # for interface formatting. sym_state := case value of { "none " : [[1, -1, -1, -1], [-1, -1, -1, -1]] "all " : [[1, 1, 1, 1], [1, 1, 1, 1]] } 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] ] if value == "all " then sym_image_next :=: sym_image_current every col := 1 to 4 do every row := 1 to 2 do place(symmet_xpos, symmet_yoff, col - 1, row - 1, sym_image_current[row, col]) return end # symmetry buttons procedure symmet_cb(vidget, e) local col, row, symcount if e === (&lpress | &rpress | &mpress) then { col := (&x - symmet_xpos) / IconSize + 1 row := (&y - symmet_yoff) / IconSize + 1 sym_state[row, col] *:= -1 sym_image_current[row, col] :=: sym_image_next[row, col] place(symmet_xpos, symmet_yoff, 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 { "new @N" : new_tile() "info @I" : tile_info() } return end # show information about tile procedure tile_info() local line1, line2, pattern, bits, density pattern := rows2pat(grid_rows) bits := tilebits(grid_rows) density := left(bits / real(*grid_rows[1] * *grid_rows), 6) line1 := left(*grid_rows[1] || "x" || *grid_rows || " b=" || bits || " d=" || density, InfoLength) line2 := if *pattern > InfoLength then pattern[1+:(InfoLength - 3)] || "..." else left(pattern, InfoLength) Notice(line1, line2) return end # undo transformation procedure undo_xform() grid_rows := pat2rows(old_pat) return setup() end # write pattern procedure write_tile() local output repeat { if SaveDialog("Write pattern") == "Cancel" then fail output := open(dialog_value, "w") | { Notice("Cannot open file for writing.") next } write(output, rows2pat(grid_rows)) close(output) return } end # write rows procedure write_rows() local output repeat { if SaveDialog("Write rows") == "Cancel" then fail output := open(dialog_value, "w") | { Notice("Cannot open file for writing.") next } every write(output, !grid_rows) close(output) 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(grid_rows, -1, "h") 4: pflip(grid_rows, "r") 5: pflip(grid_rows, "l") 7: protate(grid_rows, 90) 8: protate(grid_rows, -90) 10: list(vbits, repl("0", hbits)) 12: ptrim(grid_rows) 14: { 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, grid_rows) pcrop ! result } } } default: fail } 1: case row of { 0: pshift(grid_rows, -1, "v") 2: pshift(grid_rows, 1, "v") 4: pflip(grid_rows, "v") 5: pflip(grid_rows, "h") 7: protate(grid_rows, 180) 10: pinvert(grid_rows) 12: { 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, grid_rows) pborder ! result } } } default: fail } 2: case row of { 1: pshift(grid_rows, 1, "h") 10: pscramble(grid_rows, "b") 12: { case Dialog("Center:", ["width", "height"], [*grid_rows[1], *grid_rows], 3, ["Okay", "Cancel"]) of { "Cancel": fail "Okay": { icheck(dialog_value) | fail result := copy(params := dialog_value) push(result, grid_rows) pcenter ! result } } } default: fail } default: fail } end # transformation buttons procedure xform_cb(vidget, e) local col, row if e === (&lpress | &rpress | &mpress) then { old_pat := rows2pat(grid_rows) col := (&x - xform_xpos) / IconSize row := (&y - xform_ypos) / IconSize grid_rows := xform(col, row) | fail return setup() } end #===<>=== modify using vib; do not remove this marker line procedure grid_ui_atts() return ["size=635,568", "bg=pale gray", "label=Drawdown Editor"] end procedure grid_ui(win, cbk) return vsetup(win, cbk, ["grid_ui:Sizer:::0,0,635,568:Drawdown Editor",], ["file:Menu:pull::0,0,36,21:File",grid_file_cb, ["read @R","open @O","ims @M","write @W","copy @C", "paste @P","quit @Q ","save @S"]], ["line1:Line:::0,22,660,22:",], ["symmetries:Label:::22,316,70,13:symmetries",], ["symstate:Choice::2:26,384,64,42:",symstate_cb, ["all ","none "]], ["tile:Menu:pull::38,0,64,21:Drawdown",tile_cb, ["new @N","info @I"]], ["transformations:Label:::5,33,105,13:transformations",], ["symregion:Rect:grooved::24,338,68,36:",symmet_cb], ["info:Rect:invisible::123,32,251,19:",], ["xform:Rect:grooved::32,58,52,244:",xform_cb], ["grid:Rect:sunken::123,58,500,500:",grid_cb], ) end #===<>=== end of section maintained by vib