home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v941.tgz
/
icon.v941src.tar
/
icon.v941src
/
ipl
/
gprogs
/
penelope.icn
< prev
next >
Wrap
Text File
|
2001-06-10
|
31KB
|
1,257 lines
############################################################################
#
# 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